From bc9248cad132fa01dd2b641b6b22849bc7a05457 Mon Sep 17 00:00:00 2001 From: Fadi Hanna Date: Mon, 1 Apr 2019 12:07:47 -0700 Subject: [PATCH] Enable R2R compilation/inlining of PInvoke stubs where no marshalling is required (#22560) * These changes enable the inlining of some PInvokes that do not require any marshalling. With inlined pinvokes, R2R performance should become slightly better, since we'll avoid jitting some of the pinvoke IL stubs that we jit today for S.P.CoreLib. Performance gains not yet measured. * Added JIT_PInvokeBegin/End helpers for all architectures. Linux stubs not yet implemented * Add INLINE_GETTHREAD for arm/arm64 * Set CORJIT_FLAG_USE_PINVOKE_HELPERS jit flag for ReadyToRun compilations * Updating R2RDump tool to handle pinvokes --- src/inc/corcompile.h | 3 +- src/inc/jithelpers.h | 6 +-- src/inc/readytorun.h | 8 ++- src/inc/readytorunhelpers.h | 4 ++ src/tools/r2rdump/R2RConstants.cs | 6 +++ src/tools/r2rdump/R2RSignature.cs | 13 +++++ src/vm/CMakeLists.txt | 2 + src/vm/amd64/PInvokeStubs.asm | 82 ++++++++++++++++++++++++++++ src/vm/amd64/asmconstants.h | 18 +++++++ src/vm/amd64/pinvokestubs.S | 24 +++++++++ src/vm/arm/PInvokeStubs.asm | 90 +++++++++++++++++++++++++++++++ src/vm/arm/asmconstants.h | 21 +++++++- src/vm/arm/asmmacros.h | 36 +++++++++++++ src/vm/arm/pinvokestubs.S | 24 +++++++++ src/vm/arm64/PInvokeStubs.asm | 87 ++++++++++++++++++++++++++++++ src/vm/arm64/asmconstants.h | 17 ++++++ src/vm/arm64/asmmacros.h | 48 +++++++++++++++++ src/vm/arm64/pinvokestubs.S | 24 +++++++++ src/vm/dllimport.cpp | 8 +++ src/vm/frames.h | 1 - src/vm/gccover.cpp | 4 -- src/vm/i386/AsmMacros.inc | 23 ++++++++ src/vm/i386/PInvokeStubs.asm | 111 ++++++++++++++++++++++++++++++++++++++ src/vm/i386/asmconstants.h | 17 ++++++ src/vm/i386/pinvokestubs.S | 31 +++++++++++ src/vm/jithelpers.cpp | 40 +++++++++++++- src/vm/jitinterface.cpp | 22 ++++++-- src/vm/method.cpp | 7 +++ src/vm/methodtable.cpp | 48 +++++++++++++++++ src/vm/methodtable.h | 10 +++- src/vm/zapsig.cpp | 28 +++++++--- src/zap/zapinfo.cpp | 25 ++++++--- src/zap/zapreadytorun.cpp | 2 + 33 files changed, 857 insertions(+), 33 deletions(-) create mode 100644 src/vm/i386/AsmMacros.inc create mode 100644 src/vm/i386/PInvokeStubs.asm create mode 100644 src/vm/i386/pinvokestubs.S diff --git a/src/inc/corcompile.h b/src/inc/corcompile.h index f8f522c..743791c 100644 --- a/src/inc/corcompile.h +++ b/src/inc/corcompile.h @@ -698,13 +698,14 @@ enum CORCOMPILE_FIXUP_BLOB_KIND ENCODE_DECLARINGTYPE_HANDLE, + ENCODE_INDIRECT_PINVOKE_TARGET, /* For calling a pinvoke method ptr */ + ENCODE_MODULE_HANDLE = 0x50, /* Module token */ ENCODE_STATIC_FIELD_ADDRESS, /* For accessing a static field */ ENCODE_MODULE_ID_FOR_STATICS, /* For accessing static fields */ ENCODE_MODULE_ID_FOR_GENERIC_STATICS, /* For accessing static fields */ ENCODE_CLASS_ID_FOR_STATICS, /* For accessing static fields */ ENCODE_SYNC_LOCK, /* For synchronizing access to a type */ - ENCODE_INDIRECT_PINVOKE_TARGET, /* For calling a pinvoke method ptr */ ENCODE_PROFILING_HANDLE, /* For the method's profiling counter */ ENCODE_VARARGS_METHODDEF, /* For calling a varargs method */ ENCODE_VARARGS_METHODREF, diff --git a/src/inc/jithelpers.h b/src/inc/jithelpers.h index f305a4d..2b362a7 100644 --- a/src/inc/jithelpers.h +++ b/src/inc/jithelpers.h @@ -159,7 +159,7 @@ JITHELPER(CORINFO_HELP_VERIFICATION_RUNTIME_CHECK, JIT_VerificationRuntimeCheck,CORINFO_HELP_SIG_REG_ONLY) // GC support - DYNAMICJITHELPER(CORINFO_HELP_STOP_FOR_GC, JIT_RareDisableHelper,CORINFO_HELP_SIG_REG_ONLY) + DYNAMICJITHELPER(CORINFO_HELP_STOP_FOR_GC, JIT_RareDisableHelper, CORINFO_HELP_SIG_REG_ONLY) #ifdef ENABLE_FAST_GCPOLL_HELPER DYNAMICJITHELPER(CORINFO_HELP_POLL_GC, JIT_PollGC, CORINFO_HELP_SIG_REG_ONLY) #else @@ -354,8 +354,8 @@ JITHELPER(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, JIT_ThrowPlatformNotSupportedException, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, JIT_ThrowTypeNotSupportedException, CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_JIT_PINVOKE_BEGIN, NULL, CORINFO_HELP_SIG_UNDEF) - JITHELPER(CORINFO_HELP_JIT_PINVOKE_END, NULL, CORINFO_HELP_SIG_UNDEF) + JITHELPER(CORINFO_HELP_JIT_PINVOKE_BEGIN, JIT_PInvokeBegin, CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_JIT_PINVOKE_END, JIT_PInvokeEnd, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER, NULL, CORINFO_HELP_SIG_UNDEF) JITHELPER(CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT, NULL, CORINFO_HELP_SIG_UNDEF) diff --git a/src/inc/readytorun.h b/src/inc/readytorun.h index 4ca7528..d80a80b 100644 --- a/src/inc/readytorun.h +++ b/src/inc/readytorun.h @@ -15,7 +15,7 @@ #define READYTORUN_SIGNATURE 0x00525452 // 'RTR' -#define READYTORUN_MAJOR_VERSION 0x0002 +#define READYTORUN_MAJOR_VERSION 0x0003 #define READYTORUN_MINOR_VERSION 0x0002 // R2R Version 2.1 adds the READYTORUN_SECTION_INLINING_INFO section // R2R Version 2.2 adds the READYTORUN_SECTION_PROFILEDATA_INFO section @@ -180,6 +180,8 @@ enum ReadyToRunFixupKind READYTORUN_FIXUP_DelegateCtor = 0x2C, /* optimized delegate ctor */ READYTORUN_FIXUP_DeclaringTypeHandle = 0x2D, + + READYTORUN_FIXUP_IndirectPInvokeTarget = 0x2E, /* Target of an inlined pinvoke */ }; // @@ -230,6 +232,10 @@ enum ReadyToRunHelper READYTORUN_HELPER_MemSet = 0x40, READYTORUN_HELPER_MemCpy = 0x41, + // PInvoke helpers + READYTORUN_HELPER_PInvokeBegin = 0x42, + READYTORUN_HELPER_PInvokeEnd = 0x43, + // Get string handle lazily READYTORUN_HELPER_GetString = 0x50, diff --git a/src/inc/readytorunhelpers.h b/src/inc/readytorunhelpers.h index ffddab3..a8c2327 100644 --- a/src/inc/readytorunhelpers.h +++ b/src/inc/readytorunhelpers.h @@ -111,5 +111,9 @@ HELPER(READYTORUN_HELPER_CheckedWriteBarrier_EBP, CORINFO_HELP_CHECKED_ASSIGN_ HELPER(READYTORUN_HELPER_EndCatch, CORINFO_HELP_ENDCATCH, OPTIMIZEFORSIZE) #endif +HELPER(READYTORUN_HELPER_PInvokeBegin, CORINFO_HELP_JIT_PINVOKE_BEGIN, ) +HELPER(READYTORUN_HELPER_PInvokeEnd, CORINFO_HELP_JIT_PINVOKE_END, ) + + #undef HELPER #undef OPTIMIZEFORSPEED diff --git a/src/tools/r2rdump/R2RConstants.cs b/src/tools/r2rdump/R2RConstants.cs index 7e1c5e3..7f2b2e3 100644 --- a/src/tools/r2rdump/R2RConstants.cs +++ b/src/tools/r2rdump/R2RConstants.cs @@ -120,6 +120,8 @@ namespace R2RDump READYTORUN_FIXUP_DelegateCtor = 0x2C, /* optimized delegate ctor */ READYTORUN_FIXUP_DeclaringTypeHandle = 0x2D, + + READYTORUN_FIXUP_IndirectPInvokeTarget = 0x2E, /* Target of an inlined pinvoke */ } // @@ -171,6 +173,10 @@ namespace R2RDump READYTORUN_HELPER_MemSet = 0x40, READYTORUN_HELPER_MemCpy = 0x41, + // PInvoke helpers + READYTORUN_HELPER_PInvokeBegin = 0x42, + READYTORUN_HELPER_PInvokeEnd = 0x43, + // Get string handle lazily READYTORUN_HELPER_GetString = 0x50, diff --git a/src/tools/r2rdump/R2RSignature.cs b/src/tools/r2rdump/R2RSignature.cs index 60e4c0f..9bf4412 100644 --- a/src/tools/r2rdump/R2RSignature.cs +++ b/src/tools/r2rdump/R2RSignature.cs @@ -679,6 +679,10 @@ namespace R2RDump builder.Append(" (DECLARING_TYPE_HANDLE)"); break; + case ReadyToRunFixupKind.READYTORUN_FIXUP_IndirectPInvokeTarget: + ParseMethod(builder); + builder.Append(" (INDIRECT_PINVOKE_TARGET)"); + break; default: builder.Append(string.Format("Unknown fixup type: {0:X2}", fixupType)); @@ -1126,6 +1130,15 @@ namespace R2RDump builder.Append("MEM_CPY"); break; + // PInvoke helpers + case ReadyToRunHelper.READYTORUN_HELPER_PInvokeBegin: + builder.Append("PINVOKE_BEGIN"); + break; + + case ReadyToRunHelper.READYTORUN_HELPER_PInvokeEnd: + builder.Append("PINVOKE_END"); + break; + // Get string handle lazily case ReadyToRunHelper.READYTORUN_HELPER_GetString: builder.Append("GET_STRING"); diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt index b7ae17a..f5cbaac 100644 --- a/src/vm/CMakeLists.txt +++ b/src/vm/CMakeLists.txt @@ -679,6 +679,7 @@ elseif(CLR_CMAKE_TARGET_ARCH_I386) ${ARCH_SOURCES_DIR}/asmhelpers.asm ${ARCH_SOURCES_DIR}/gmsasm.asm ${ARCH_SOURCES_DIR}/jithelp.asm + ${ARCH_SOURCES_DIR}/PInvokeStubs.asm ) set(VM_HEADERS_WKS_ARCH_ASM @@ -735,6 +736,7 @@ else(WIN32) ${ARCH_SOURCES_DIR}/asmhelpers.S ${ARCH_SOURCES_DIR}/jithelp.S ${ARCH_SOURCES_DIR}/gmsasm.S + ${ARCH_SOURCES_DIR}/pinvokestubs.S ${ARCH_SOURCES_DIR}/umthunkstub.S ) elseif(CLR_CMAKE_TARGET_ARCH_ARM) diff --git a/src/vm/amd64/PInvokeStubs.asm b/src/vm/amd64/PInvokeStubs.asm index 82e33ac..8969ea9 100644 --- a/src/vm/amd64/PInvokeStubs.asm +++ b/src/vm/amd64/PInvokeStubs.asm @@ -13,6 +13,14 @@ include AsmConstants.inc extern GenericPInvokeCalliStubWorker:proc extern VarargPInvokeStubWorker:proc +extern JIT_PInvokeEndRarePath:proc + +extern s_gsCookie:QWORD +extern ??_7InlinedCallFrame@@6B@:QWORD +extern g_TrapReturningThreads:DWORD + +; Min amount of stack space that a nested function should allocate. +MIN_SIZE equ 28h ; ; in: @@ -135,4 +143,78 @@ NESTED_ENTRY VarargPInvokeGenILStub, _TEXT NESTED_END VarargPInvokeGenILStub, _TEXT +; +; in: +; InlinedCallFrame (rcx) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +; before actual InlinedCallFrame data) +; +; +LEAF_ENTRY JIT_PInvokeBegin, _TEXT + + mov rax, qword ptr [s_gsCookie] + mov qword ptr [rcx], rax + add rcx, SIZEOF_GSCookie + + ;; set first slot to the value of InlinedCallFrame::`vftable' (checked by runtime code) + lea rax,[??_7InlinedCallFrame@@6B@] + mov qword ptr [rcx], rax + + mov qword ptr [rcx + OFFSETOF__InlinedCallFrame__m_Datum], 0 + + mov rax, rsp + add rax, 8 + mov qword ptr [rcx + OFFSETOF__InlinedCallFrame__m_pCallSiteSP], rax + mov qword ptr [rcx + OFFSETOF__InlinedCallFrame__m_pCalleeSavedFP], rbp + + mov rax, [rsp] + mov qword ptr [rcx + OFFSETOF__InlinedCallFrame__m_pCallerReturnAddress], rax + + INLINE_GETTHREAD rax + ;; pFrame->m_Next = pThread->m_pFrame; + mov rdx, qword ptr [rax + OFFSETOF__Thread__m_pFrame] + mov qword ptr [rcx + OFFSETOF__Frame__m_Next], rdx + + ;; pThread->m_pFrame = pFrame; + mov qword ptr [rax + OFFSETOF__Thread__m_pFrame], rcx + + ;; pThread->m_fPreemptiveGCDisabled = 0 + mov dword ptr [rax + OFFSETOF__Thread__m_fPreemptiveGCDisabled], 0 + + ret + +LEAF_END JIT_PInvokeBegin, _TEXT + +; +; in: +; InlinedCallFrame (rcx) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +; before actual InlinedCallFrame data) +; +; +LEAF_ENTRY JIT_PInvokeEnd, _TEXT + + add rcx, SIZEOF_GSCookie + + INLINE_GETTHREAD rdx + + ;; rcx = pFrame + ;; rdx = pThread + + ;; pThread->m_fPreemptiveGCDisabled = 1 + mov dword ptr [rdx + OFFSETOF__Thread__m_fPreemptiveGCDisabled], 1 + + ;; Check return trap + cmp [g_TrapReturningThreads], 0 + jnz RarePath + + ;; pThread->m_pFrame = pFrame->m_Next + mov rax, qword ptr [rcx + OFFSETOF__Frame__m_Next] + mov qword ptr [rdx + OFFSETOF__Thread__m_pFrame], rax + + ret + +RarePath: + jmp JIT_PInvokeEndRarePath + +LEAF_END JIT_PInvokeEnd, _TEXT + end diff --git a/src/vm/amd64/asmconstants.h b/src/vm/amd64/asmconstants.h index 1af0786..10a5ea0 100644 --- a/src/vm/amd64/asmconstants.h +++ b/src/vm/amd64/asmconstants.h @@ -556,6 +556,24 @@ ASMCONSTANTS_C_ASSERT(OFFSETOF__StringObject__m_StringLength ASMCONSTANTS_C_ASSERT(OFFSETOF__ArrayTypeDesc__m_Arg == offsetof(ArrayTypeDesc, m_Arg)); +// For JIT_PInvokeBegin and JIT_PInvokeEnd helpers +#define OFFSETOF__InlinedCallFrame__m_Datum 0x10 +ASMCONSTANTS_C_ASSERT(OFFSETOF__InlinedCallFrame__m_Datum + == offsetof(InlinedCallFrame, m_Datum)); + +#define OFFSETOF__InlinedCallFrame__m_pCallSiteSP 0x20 +ASMCONSTANTS_C_ASSERT(OFFSETOF__InlinedCallFrame__m_pCallSiteSP + == offsetof(InlinedCallFrame, m_pCallSiteSP)); + +#define OFFSETOF__InlinedCallFrame__m_pCallerReturnAddress 0x28 +ASMCONSTANTS_C_ASSERT(OFFSETOF__InlinedCallFrame__m_pCallerReturnAddress + == offsetof(InlinedCallFrame, m_pCallerReturnAddress)); + +#define OFFSETOF__InlinedCallFrame__m_pCalleeSavedFP 0x30 +ASMCONSTANTS_C_ASSERT(OFFSETOF__InlinedCallFrame__m_pCalleeSavedFP + == offsetof(InlinedCallFrame, m_pCalleeSavedFP)); + + #define CallDescrData__pSrc 0x00 #define CallDescrData__numStackSlots 0x08 #ifdef UNIX_AMD64_ABI diff --git a/src/vm/amd64/pinvokestubs.S b/src/vm/amd64/pinvokestubs.S index 1952164..fceb7f0 100644 --- a/src/vm/amd64/pinvokestubs.S +++ b/src/vm/amd64/pinvokestubs.S @@ -127,3 +127,27 @@ NESTED_ENTRY VarargPInvokeGenILStub, _TEXT, NoHandler jmp C_FUNC(VarargPInvokeStubHelper) NESTED_END VarargPInvokeGenILStub, _TEXT + +// +// IN: +// InlinedCallFrame (rcx) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +// before actual InlinedCallFrame data) +// +// +LEAF_ENTRY JIT_PInvokeBegin, _TEXT + // Not yet supported + int 3 + ret +LEAF_END JIT_PInvokeBegin, _TEXT + +// +// IN: +// InlinedCallFrame (rcx) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +// before actual InlinedCallFrame data) +// +// +LEAF_ENTRY JIT_PInvokeEnd, _TEXT + // Not yet supported + int 3 + ret +LEAF_END JIT_PInvokeEnd, _TEXT diff --git a/src/vm/arm/PInvokeStubs.asm b/src/vm/arm/PInvokeStubs.asm index 791d66a..2e129a8 100644 --- a/src/vm/arm/PInvokeStubs.asm +++ b/src/vm/arm/PInvokeStubs.asm @@ -16,7 +16,13 @@ IMPORT VarargPInvokeStubWorker IMPORT GenericPInvokeCalliStubWorker + IMPORT JIT_PInvokeEndRarePath + IMPORT s_gsCookie + IMPORT g_TrapReturningThreads + + SETALIAS InlinedCallFrame_vftable, ??_7InlinedCallFrame@@6B@ + IMPORT $InlinedCallFrame_vftable ; ------------------------------------------------------------------ ; Macro to generate PInvoke Stubs. @@ -105,8 +111,92 @@ __PInvokeGenStubFuncName SETS "$__PInvokeGenStubFuncName":CC:"_RetBuffArg" MEND + TEXTAREA ; ------------------------------------------------------------------ +; JIT_PInvokeBegin helper +; +; in: +; r0 = InlinedCallFrame*: pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +; before actual InlinedCallFrame data) +; + LEAF_ENTRY JIT_PInvokeBegin + + ldr r1, =s_gsCookie + ldr r1, [r1] + str r1, [r0] + add r0, r0, SIZEOF__GSCookie + + ;; r0 = pFrame + + ;; set first slot to the value of InlinedCallFrame::`vftable' (checked by runtime code) + ldr r1, =$InlinedCallFrame_vftable + str r1, [r0] + + mov r1, 0 + str r1, [r0, #InlinedCallFrame__m_Datum] + + str sp, [r0, #InlinedCallFrame__m_pCallSiteSP] + str r11, [r0, #InlinedCallFrame__m_pCalleeSavedFP] + str lr, [r0, #InlinedCallFrame__m_pCallerReturnAddress] + + ;; r1 = GetThread(), TRASHES r2 + INLINE_GETTHREAD r1, r2 + + ;; pFrame->m_Next = pThread->m_pFrame; + ldr r2, [r1, #Thread_m_pFrame] + str r2, [r0, #Frame__m_Next] + + ;; pThread->m_pFrame = pFrame; + str r0, [r1, #Thread_m_pFrame] + + ;; pThread->m_fPreemptiveGCDisabled = 0 + mov r2, 0 + str r2, [r1, #Thread_m_fPreemptiveGCDisabled] + + bx lr + + LEAF_END + +; ------------------------------------------------------------------ +; JIT_PInvokeEnd helper +; +; in: +; r0 = InlinedCallFrame* +; + LEAF_ENTRY JIT_PInvokeEnd + + add r0, r0, SIZEOF__GSCookie + + ;; r1 = GetThread(), TRASHES r2 + INLINE_GETTHREAD r1, r2 + + ;; r0 = pFrame + ;; r1 = pThread + + ;; pThread->m_fPreemptiveGCDisabled = 1 + mov r2, 1 + str r2, [r1, #Thread_m_fPreemptiveGCDisabled] + + ;; Check return trap + ldr r2, =g_TrapReturningThreads + ldr r2, [r2] + cbnz r2, RarePath + + ;; pThread->m_pFrame = pFrame->m_Next + ldr r2, [r0, #Frame__m_Next] + str r2, [r1, #Thread_m_pFrame] + + bx lr + +RarePath + b JIT_PInvokeEndRarePath + + LEAF_END + + INLINE_GETTHREAD_CONSTANT_POOL + +; ------------------------------------------------------------------ ; VarargPInvokeStub & VarargPInvokeGenILStub ; There is a separate stub when the method has a hidden return buffer arg. ; diff --git a/src/vm/arm/asmconstants.h b/src/vm/arm/asmconstants.h index c57c92a..9cb85a5 100644 --- a/src/vm/arm/asmconstants.h +++ b/src/vm/arm/asmconstants.h @@ -229,8 +229,25 @@ ASMCONSTANTS_C_ASSERT(CallDescrData__returnValue == offsetof(CallDescrD #define SIZEOF__FaultingExceptionFrame (SIZEOF__Frame + 0x8 + SIZEOF__CONTEXT) #define FaultingExceptionFrame__m_fFilterExecuted SIZEOF__Frame -ASMCONSTANTS_C_ASSERT(SIZEOF__FaultingExceptionFrame == sizeof(FaultingExceptionFrame)); -ASMCONSTANTS_C_ASSERT(FaultingExceptionFrame__m_fFilterExecuted == offsetof(FaultingExceptionFrame, m_fFilterExecuted)); +ASMCONSTANTS_C_ASSERT(SIZEOF__FaultingExceptionFrame == sizeof(FaultingExceptionFrame)) +ASMCONSTANTS_C_ASSERT(FaultingExceptionFrame__m_fFilterExecuted == offsetof(FaultingExceptionFrame, m_fFilterExecuted)) + +// For JIT_PInvokeBegin and JIT_PInvokeEnd helpers +#define Frame__m_Next 0x04 +ASMCONSTANTS_C_ASSERT(Frame__m_Next == offsetof(Frame, m_Next)) + +#define InlinedCallFrame__m_Datum 0x08 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_Datum == offsetof(InlinedCallFrame, m_Datum)) + +#define InlinedCallFrame__m_pCallSiteSP 0x0C +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCallSiteSP == offsetof(InlinedCallFrame, m_pCallSiteSP)) + +#define InlinedCallFrame__m_pCallerReturnAddress 0x10 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCallerReturnAddress == offsetof(InlinedCallFrame, m_pCallerReturnAddress)) + +#define InlinedCallFrame__m_pCalleeSavedFP 0x14 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCalleeSavedFP == offsetof(InlinedCallFrame, m_pCalleeSavedFP)) + #undef ASMCONSTANTS_RUNTIME_ASSERT #undef ASMCONSTANTS_C_ASSERT diff --git a/src/vm/arm/asmmacros.h b/src/vm/arm/asmmacros.h index cff79c2..97a6b80 100644 --- a/src/vm/arm/asmmacros.h +++ b/src/vm/arm/asmmacros.h @@ -159,3 +159,39 @@ __PWTB_StackAlloc SETA __PWTB_TransitionBlock EPILOG_RETURN MEND +;----------------------------------------------------------------------------- +; Macro to get a pointer to the Thread* object for the currently executing thread +; +__tls_array equ 0x2C ;; offsetof(TEB, ThreadLocalStoragePointer) + + GBLS __SECTIONREL_gCurrentThreadInfo +__SECTIONREL_gCurrentThreadInfo SETS "SECTIONREL_gCurrentThreadInfo" + + MACRO + INLINE_GETTHREAD $destReg, $trashReg + EXTERN _tls_index + + ldr $destReg, =_tls_index + ldr $destReg, [$destReg] + mrc p15, 0, $trashReg, c13, c0, 2 + ldr $trashReg, [$trashReg, #__tls_array] + ldr $destReg, [$trashReg, $destReg, lsl #2] + ldr $trashReg, $__SECTIONREL_gCurrentThreadInfo + ldr $destReg,[$destReg, $trashReg] ; return gCurrentThreadInfo.m_pThread + MEND + +;----------------------------------------------------------------------------- +; INLINE_GETTHREAD_CONSTANT_POOL macro has to be used after the last function in the .asm file that used +; INLINE_GETTHREAD. Optionally, it can be also used after any function that used INLINE_GETTHREAD +; to improve density, or to reduce distance betweeen the constant pool and its use. +; + MACRO + INLINE_GETTHREAD_CONSTANT_POOL + EXTERN gCurrentThreadInfo + +$__SECTIONREL_gCurrentThreadInfo + DCDU gCurrentThreadInfo + RELOC 15 ;; SECREL + +__SECTIONREL_gCurrentThreadInfo SETS "$__SECTIONREL_gCurrentThreadInfo":CC:"_" + MEND diff --git a/src/vm/arm/pinvokestubs.S b/src/vm/arm/pinvokestubs.S index 2027925..008441f 100644 --- a/src/vm/arm/pinvokestubs.S +++ b/src/vm/arm/pinvokestubs.S @@ -76,6 +76,30 @@ .endmacro // ------------------------------------------------------------------ +// IN: +// InlinedCallFrame (r0) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +// before actual InlinedCallFrame data) +// +// + LEAF_ENTRY JIT_PInvokeBegin, _TEXT + // Not yet supported + EMIT_BREAKPOINT + bx lr + LEAF_END JIT_PInvokeBegin, _TEXT + +// ------------------------------------------------------------------ +// IN: +// InlinedCallFrame (r0) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +// before actual InlinedCallFrame data) +// +// + LEAF_ENTRY JIT_PInvokeEnd, _TEXT + // Not yet supported + EMIT_BREAKPOINT + bx lr + LEAF_END JIT_PInvokeEnd, _TEXT + +// ------------------------------------------------------------------ // VarargPInvokeStub & VarargPInvokeGenILStub // There is a separate stub when the method has a hidden return buffer arg. // diff --git a/src/vm/arm64/PInvokeStubs.asm b/src/vm/arm64/PInvokeStubs.asm index 440af92..87cb77f 100644 --- a/src/vm/arm64/PInvokeStubs.asm +++ b/src/vm/arm64/PInvokeStubs.asm @@ -16,6 +16,13 @@ IMPORT VarargPInvokeStubWorker IMPORT GenericPInvokeCalliStubWorker + IMPORT JIT_PInvokeEndRarePath + + IMPORT s_gsCookie + IMPORT g_TrapReturningThreads + + SETALIAS InlinedCallFrame_vftable, ??_7InlinedCallFrame@@6B@ + IMPORT $InlinedCallFrame_vftable ; ------------------------------------------------------------------ @@ -107,9 +114,89 @@ __PInvokeStubWorkerName SETS "$FuncPrefix":CC:"StubWorker" MEND + TEXTAREA ; ------------------------------------------------------------------ +; JIT_PInvokeBegin helper +; +; in: +; x0 = InlinedCallFrame* +; + LEAF_ENTRY JIT_PInvokeBegin + + ldr x9, =s_gsCookie + ldr x9, [x9] + str x9, [x0] + add x10, x0, SIZEOF__GSCookie + + ;; set first slot to the value of InlinedCallFrame::`vftable' (checked by runtime code) + ldr x9, =$InlinedCallFrame_vftable + str x9, [x10] + + str xzr, [x10, #InlinedCallFrame__m_Datum] + + mov x9, sp + str x9, [x10, #InlinedCallFrame__m_pCallSiteSP] + str fp, [x10, #InlinedCallFrame__m_pCalleeSavedFP] + str lr, [x10, #InlinedCallFrame__m_pCallerReturnAddress] + + ;; x0 = GetThread(), TRASHES x9 + INLINE_GETTHREAD x0, x9 + + ;; pFrame->m_Next = pThread->m_pFrame; + ldr x9, [x0, #Thread_m_pFrame] + str x9, [x10, #Frame__m_Next] + + ;; pThread->m_pFrame = pFrame; + str x10, [x0, #Thread_m_pFrame] + + ;; pThread->m_fPreemptiveGCDisabled = 0 + str wzr, [x0, #Thread_m_fPreemptiveGCDisabled] + + ret + + LEAF_END + +; ------------------------------------------------------------------ +; JIT_PInvokeEnd helper +; +; in: +; x0 = InlinedCallFrame* +; + LEAF_ENTRY JIT_PInvokeEnd + + add x0, x0, SIZEOF__GSCookie + + ;; x1 = GetThread(), TRASHES x2 + INLINE_GETTHREAD x1, x2 + + ;; x0 = pFrame + ;; x1 = pThread + + ;; pThread->m_fPreemptiveGCDisabled = 1 + mov x9, 1 + str w9, [x1, #Thread_m_fPreemptiveGCDisabled] + + ;; Check return trap + ldr x9, =g_TrapReturningThreads + ldr x9, [x9] + cbnz x9, RarePath + + ;; pThread->m_pFrame = pFrame->m_Next + ldr x9, [x0, #Frame__m_Next] + str x9, [x1, #Thread_m_pFrame] + + ret + +RarePath + b JIT_PInvokeEndRarePath + + LEAF_END + + INLINE_GETTHREAD_CONSTANT_POOL + +; ------------------------------------------------------------------ ; VarargPInvokeStub & VarargPInvokeGenILStub ; ; in: diff --git a/src/vm/arm64/asmconstants.h b/src/vm/arm64/asmconstants.h index 1acc1b4..d2df47b 100644 --- a/src/vm/arm64/asmconstants.h +++ b/src/vm/arm64/asmconstants.h @@ -210,5 +210,22 @@ ASMCONSTANTS_C_ASSERT(DomainLocalModule__m_pDataBlob == offsetof(DomainLocalModu ASMCONSTANTS_C_ASSERT(DomainLocalModule__m_pGCStatics == offsetof(DomainLocalModule, m_pGCStatics)); +// For JIT_PInvokeBegin and JIT_PInvokeEnd helpers +#define Frame__m_Next 0x08 +ASMCONSTANTS_C_ASSERT(Frame__m_Next == offsetof(Frame, m_Next)) + +#define InlinedCallFrame__m_Datum 0x10 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_Datum == offsetof(InlinedCallFrame, m_Datum)) + +#define InlinedCallFrame__m_pCallSiteSP 0x20 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCallSiteSP == offsetof(InlinedCallFrame, m_pCallSiteSP)) + +#define InlinedCallFrame__m_pCallerReturnAddress 0x28 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCallerReturnAddress == offsetof(InlinedCallFrame, m_pCallerReturnAddress)) + +#define InlinedCallFrame__m_pCalleeSavedFP 0x30 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCalleeSavedFP == offsetof(InlinedCallFrame, m_pCalleeSavedFP)) + + #undef ASMCONSTANTS_RUNTIME_ASSERT #undef ASMCONSTANTS_C_ASSERT diff --git a/src/vm/arm64/asmmacros.h b/src/vm/arm64/asmmacros.h index 5c6195b..f7c83a6 100644 --- a/src/vm/arm64/asmmacros.h +++ b/src/vm/arm64/asmmacros.h @@ -299,3 +299,51 @@ $__RedirectionStubEndFuncName MEND +;----------------------------------------------------------------------------- +; Macro to get a pointer to the Thread* object for the currently executing thread +; +__tls_array equ 0x58 ;; offsetof(TEB, ThreadLocalStoragePointer) + + EXTERN _tls_index + + GBLS __SECTIONREL_gCurrentThreadInfo +__SECTIONREL_gCurrentThreadInfo SETS "SECTIONREL_gCurrentThreadInfo" + + MACRO + INLINE_GETTHREAD $destReg, $trashReg + + ;; The following macro variables are just some assembler magic to get the name of the 32-bit version + ;; of $trashReg. It does it by string manipulation. Replaces something like x3 with w3. + LCLS TrashRegister32Bit +TrashRegister32Bit SETS "$trashReg" +TrashRegister32Bit SETS "w":CC:("$TrashRegister32Bit":RIGHT:((:LEN:TrashRegister32Bit) - 1)) + + ldr $trashReg, =_tls_index + ldr $TrashRegister32Bit, [$trashReg] + ldr $destReg, [xpr, #__tls_array] + ldr $destReg, [$destReg, $trashReg lsl #3] + ldr $trashReg, =$__SECTIONREL_gCurrentThreadInfo + ldr $trashReg, [$trashReg] + ldr $destReg, [$destReg, $trashReg] ; return gCurrentThreadInfo.m_pThread + MEND + +;----------------------------------------------------------------------------- +; INLINE_GETTHREAD_CONSTANT_POOL macro has to be used after the last function in the .asm file that used +; INLINE_GETTHREAD. Optionally, it can be also used after any function that used INLINE_GETTHREAD +; to improve density, or to reduce distance betweeen the constant pool and its use. +; + + MACRO + INLINE_GETTHREAD_CONSTANT_POOL + + EXTERN gCurrentThreadInfo + + ;; Section relocs are 32 bits. Using an extra DCD initialized to zero for 8-byte alignment. +$__SECTIONREL_gCurrentThreadInfo + DCD gCurrentThreadInfo + RELOC 8, gCurrentThreadInfo ;; SECREL + DCD 0 + +__SECTIONREL_gCurrentThreadInfo SETS "$__SECTIONREL_gCurrentThreadInfo":CC:"_" + + MEND diff --git a/src/vm/arm64/pinvokestubs.S b/src/vm/arm64/pinvokestubs.S index ad64db8..d0fd395 100644 --- a/src/vm/arm64/pinvokestubs.S +++ b/src/vm/arm64/pinvokestubs.S @@ -87,6 +87,30 @@ LOCAL_LABEL(\__PInvokeStubFuncName\()_0): .endm // ------------------------------------------------------------------ +// IN: +// InlinedCallFrame (x0) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +// before actual InlinedCallFrame data) +// +// + LEAF_ENTRY JIT_PInvokeBegin, _TEXT + // Not yet supported + EMIT_BREAKPOINT + ret lr + LEAF_END JIT_PInvokeBegin, _TEXT + +// ------------------------------------------------------------------ +// IN: +// InlinedCallFrame (x0) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +// before actual InlinedCallFrame data) +// +// + LEAF_ENTRY JIT_PInvokeEnd, _TEXT + // Not yet supported + EMIT_BREAKPOINT + ret lr + LEAF_END JIT_PInvokeEnd, _TEXT + +// ------------------------------------------------------------------ // VarargPInvokeStub & VarargPInvokeGenILStub // // in: diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp index c83f05c..5fa9ac4 100644 --- a/src/vm/dllimport.cpp +++ b/src/vm/dllimport.cpp @@ -3472,6 +3472,14 @@ BOOL NDirect::MarshalingRequired(MethodDesc *pMD, PCCOR_SIGNATURE pSig /*= NULL* return TRUE; } +#ifdef FEATURE_READYTORUN_COMPILER + if (IsReadyToRunCompilation()) + { + if (!hndArgType.AsMethodTable()->IsLayoutInCurrentVersionBubble()) + return TRUE; + } +#endif + // return value is fine as long as it can be normalized to an integer if (i == 0) { diff --git a/src/vm/frames.h b/src/vm/frames.h index 3d092bd..5cc5e37 100644 --- a/src/vm/frames.h +++ b/src/vm/frames.h @@ -3002,7 +3002,6 @@ public: PTR_VOID m_StubSecretArg; #endif // _WIN64 -protected: // X86: ESP after pushing the outgoing arguments, and just before calling // out to unmanaged code. // Other platforms: the field stays set throughout the declaring method. diff --git a/src/vm/gccover.cpp b/src/vm/gccover.cpp index 6b2e7a6..a080289 100644 --- a/src/vm/gccover.cpp +++ b/src/vm/gccover.cpp @@ -388,11 +388,7 @@ public: // // Similarly, inserting breakpoints can be avoided for JIT_PollGC() and JIT_StressGC(). -#if defined(_TARGET_ARM_) || defined(_TARGET_AMD64_) extern "C" FCDECL0(VOID, JIT_RareDisableHelper); -#else -FCDECL0(VOID, JIT_RareDisableHelper); -#endif /****************************************************************************/ /* sprinkle interupt instructions that will stop on every GCSafe location diff --git a/src/vm/i386/AsmMacros.inc b/src/vm/i386/AsmMacros.inc new file mode 100644 index 0000000..86ce6ac --- /dev/null +++ b/src/vm/i386/AsmMacros.inc @@ -0,0 +1,23 @@ +; 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. + +; +; Define macros to build unwind data for prologues. +; + +__tls_array equ 2Ch ;; offsetof(TEB, ThreadLocalStoragePointer) + + +INLINE_GETTHREAD macro destReg, trashReg + ASSUME fs : NOTHING + + EXTERN __tls_index:DWORD + EXTERN _gCurrentThreadInfo:DWORD + + mov destReg, [__tls_index] + mov trashReg, fs:[__tls_array] + mov trashReg, [trashReg + destReg * 4] + add trashReg, SECTIONREL _gCurrentThreadInfo + mov destReg, [trashReg] +endm diff --git a/src/vm/i386/PInvokeStubs.asm b/src/vm/i386/PInvokeStubs.asm new file mode 100644 index 0000000..7295c56 --- /dev/null +++ b/src/vm/i386/PInvokeStubs.asm @@ -0,0 +1,111 @@ +; 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. + +; *********************************************************************** +; File: PInvokeStubs.asm +; +; *********************************************************************** +; +; *** NOTE: If you make changes to this file, propagate the changes to +; PInvokeStubs.s in this directory + +; This contains JITinterface routines that are 100% x86 assembly + + .586 + .model flat + + include asmconstants.inc + include asmmacros.inc + + option casemap:none + .code + +extern _s_gsCookie:DWORD +extern ??_7InlinedCallFrame@@6B@:DWORD +extern _g_TrapReturningThreads:DWORD + +extern @JIT_PInvokeEndRarePath@0:proc + +.686P +.XMM + +; +; in: +; InlinedCallFrame (ecx) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +; before actual InlinedCallFrame data) +; +; +_JIT_PInvokeBegin@4 PROC public + + mov eax, dword ptr [_s_gsCookie] + mov dword ptr [ecx], eax + add ecx, SIZEOF_GSCookie + + ;; set first slot to the value of InlinedCallFrame::`vftable' (checked by runtime code) + lea eax,[??_7InlinedCallFrame@@6B@] + mov dword ptr [ecx], eax + + mov dword ptr [ecx + InlinedCallFrame__m_Datum], 0 + + + mov eax, esp + add eax, 4 + mov dword ptr [ecx + InlinedCallFrame__m_pCallSiteSP], eax + mov dword ptr [ecx + InlinedCallFrame__m_pCalleeSavedFP], ebp + + mov eax, [esp] + mov dword ptr [ecx + InlinedCallFrame__m_pCallerReturnAddress], eax + + ;; edx = GetThread(). Trashes eax + INLINE_GETTHREAD edx, eax + + ;; pFrame->m_Next = pThread->m_pFrame; + mov eax, dword ptr [edx + Thread_m_pFrame] + mov dword ptr [ecx + Frame__m_Next], eax + + ;; pThread->m_pFrame = pFrame; + mov dword ptr [edx + Thread_m_pFrame], ecx + + ;; pThread->m_fPreemptiveGCDisabled = 0 + mov dword ptr [edx + Thread_m_fPreemptiveGCDisabled], 0 + + ret + +_JIT_PInvokeBegin@4 ENDP + +; +; in: +; InlinedCallFrame (ecx) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +; before actual InlinedCallFrame data) +; +; +_JIT_PInvokeEnd@4 PROC public + + add ecx, SIZEOF_GSCookie + + ;; edx = GetThread(). Trashes eax + INLINE_GETTHREAD edx, eax + + ;; ecx = pFrame + ;; edx = pThread + + ;; pThread->m_fPreemptiveGCDisabled = 1 + mov dword ptr [edx + Thread_m_fPreemptiveGCDisabled], 1 + + ;; Check return trap + cmp [_g_TrapReturningThreads], 0 + jnz RarePath + + ;; pThread->m_pFrame = pFrame->m_Next + mov eax, dword ptr [ecx + Frame__m_Next] + mov dword ptr [edx + Thread_m_pFrame], eax + + ret + +RarePath: + jmp @JIT_PInvokeEndRarePath@0 + +_JIT_PInvokeEnd@4 ENDP + + end diff --git a/src/vm/i386/asmconstants.h b/src/vm/i386/asmconstants.h index 9b4735b..136a31e 100644 --- a/src/vm/i386/asmconstants.h +++ b/src/vm/i386/asmconstants.h @@ -339,6 +339,23 @@ ASMCONSTANTS_C_ASSERT(Thread__m_pDomain == offsetof(Thread, m_pDomain)); #endif +// For JIT_PInvokeBegin and JIT_PInvokeEnd helpers +#define Frame__m_Next 0x04 +ASMCONSTANTS_C_ASSERT(Frame__m_Next == offsetof(Frame, m_Next)); + +#define InlinedCallFrame__m_Datum 0x08 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_Datum == offsetof(InlinedCallFrame, m_Datum)); + +#define InlinedCallFrame__m_pCallSiteSP 0x0C +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCallSiteSP == offsetof(InlinedCallFrame, m_pCallSiteSP)); + +#define InlinedCallFrame__m_pCallerReturnAddress 0x10 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCallerReturnAddress == offsetof(InlinedCallFrame, m_pCallerReturnAddress)); + +#define InlinedCallFrame__m_pCalleeSavedFP 0x14 +ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCalleeSavedFP == offsetof(InlinedCallFrame, m_pCalleeSavedFP)); + + #ifdef FEATURE_STUBS_AS_IL // DelegateObject from src/vm/object.h #define DelegateObject___target 0x04 // offset 0 is m_pMethTab of base class Object diff --git a/src/vm/i386/pinvokestubs.S b/src/vm/i386/pinvokestubs.S new file mode 100644 index 0000000..74b20b5 --- /dev/null +++ b/src/vm/i386/pinvokestubs.S @@ -0,0 +1,31 @@ +// 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" + +// +// IN: +// InlinedCallFrame (ecx) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +// before actual InlinedCallFrame data) +// +// +LEAF_ENTRY JIT_PInvokeBegin, _TEXT + // Not yet supported + int 3 + ret +LEAF_END JIT_PInvokeBegin, _TEXT + +// +// IN: +// InlinedCallFrame (ecx) = pointer to the InlinedCallFrame data, including the GS cookie slot (GS cookie right +// before actual InlinedCallFrame data) +// +// +LEAF_ENTRY JIT_PInvokeEnd, _TEXT + // Not yet supported + int 3 + ret +LEAF_END JIT_PInvokeEnd, _TEXT diff --git a/src/vm/jithelpers.cpp b/src/vm/jithelpers.cpp index 0576ca7..d30b4d3 100644 --- a/src/vm/jithelpers.cpp +++ b/src/vm/jithelpers.cpp @@ -5591,15 +5591,50 @@ HCIMPL0(VOID, JIT_PollGC) } HCIMPLEND + +/*************************************************************/ +// This helper is similar to JIT_RareDisableHelper, but has more operations +// tailored to the post-pinvoke operations. +extern "C" FCDECL0(VOID, JIT_PInvokeEndRarePath); + +HCIMPL0(void, JIT_PInvokeEndRarePath) +{ + BEGIN_PRESERVE_LAST_ERROR; + + FCALL_CONTRACT; + + Thread *thread = GetThread(); + + // We need to disable the implicit FORBID GC region that exists inside an FCALL + // in order to call RareDisablePreemptiveGC(). + FC_CAN_TRIGGER_GC(); + thread->RareDisablePreemptiveGC(); + FC_CAN_TRIGGER_GC_END(); + + FC_GC_POLL_NOT_NEEDED(); + + HELPER_METHOD_FRAME_BEGIN_NOPOLL(); // Set up a frame + thread->HandleThreadAbort(); + HELPER_METHOD_FRAME_END(); + + InlinedCallFrame* frame = (InlinedCallFrame*)thread->m_pFrame; + + thread->m_pFrame->Pop(thread); + + END_PRESERVE_LAST_ERROR; +} +HCIMPLEND + /*************************************************************/ // For an inlined N/Direct call (and possibly for other places that need this service) // we have noticed that the returning thread should trap for one reason or another. // ECall sets up the frame. +extern "C" FCDECL0(VOID, JIT_RareDisableHelper); + #if defined(_TARGET_ARM_) || defined(_TARGET_AMD64_) // The JIT expects this helper to preserve the return value on AMD64 and ARM. We should eventually // switch other platforms to the same convention since it produces smaller code. -extern "C" FCDECL0(VOID, JIT_RareDisableHelper); extern "C" FCDECL0(VOID, JIT_RareDisableHelperWorker); HCIMPL0(void, JIT_RareDisableHelperWorker) @@ -5783,6 +5818,9 @@ Thread * __stdcall JIT_InitPInvokeFrame(InlinedCallFrame *pFrame, PTR_VOID StubS #endif // _WIN64 +EXTERN_C void JIT_PInvokeBegin(InlinedCallFrame* pFrame); +EXTERN_C void JIT_PInvokeEnd(InlinedCallFrame* pFrame); + //======================================================================== // // JIT HELPERS IMPLEMENTED AS FCALLS diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp index 7c5a557..3a90262 100644 --- a/src/vm/jitinterface.cpp +++ b/src/vm/jitinterface.cpp @@ -10181,11 +10181,27 @@ void CEEInfo::getEEInfo(CORINFO_EE_INFO *pEEInfoOut) } else { - // inlinedCallFrameInfo is not used for R2R compilation + // We'll declare a fixed size to use for the inlined call frame for R2R here. The size we declare + // is currently slightly larger that the actual size of the struct, just in case we decide to add + // more fields to the struct in the future, in an effort to not completely invalidate existing R2R images. + // The assert below ensures that this fixed size is at least large enough to hold the data structures + // used at runtime. + // ** IMPORTANT ** If you ever need to change the value of this fixed size, make sure to change the R2R + // version number, otherwise older R2R images will probably crash when used. + + const int r2rInlinedCallFrameSize = TARGET_POINTER_SIZE * 11; + +#if defined(_DEBUG) && !defined(CROSSBITNESS_COMPILE) + InlinedCallFrame::GetEEInfo(&pEEInfoOut->inlinedCallFrameInfo); + _ASSERTE(pEEInfoOut->inlinedCallFrameInfo.size <= r2rInlinedCallFrameSize); +#endif + + // inlinedCallFrameInfo is mostly not used for R2R compilation (only the size field is used) ZeroMemory(&pEEInfoOut->inlinedCallFrameInfo, sizeof(pEEInfoOut->inlinedCallFrameInfo)); - pEEInfoOut->offsetOfThreadFrame = (DWORD)-1; - pEEInfoOut->offsetOfGCState = (DWORD)-1; + pEEInfoOut->offsetOfThreadFrame = (DWORD)-1; + pEEInfoOut->offsetOfGCState = (DWORD)-1; + pEEInfoOut->inlinedCallFrameInfo.size = r2rInlinedCallFrameSize; } #ifndef CROSSBITNESS_COMPILE diff --git a/src/vm/method.cpp b/src/vm/method.cpp index 70d79ca..d180014 100644 --- a/src/vm/method.cpp +++ b/src/vm/method.cpp @@ -5127,6 +5127,13 @@ FARPROC NDirectMethodDesc::FindEntryPointWithMangling(HINSTANCE hMod, PTR_CUTF8 if (IsStdCall()) { + if (GetModule()->IsReadyToRun()) + { + // Computing if marshalling is required also computes the required stack size. We need the stack size to correctly form the + // name of the import pinvoke function on x86 + ((NDirectMethodDesc*)this)->MarshalingRequired(); + } + DWORD probedEntrypointNameLength = (DWORD)(strlen(entryPointName) + 1); // 1 for null terminator int dstbufsize = (int)(sizeof(char) * (probedEntrypointNameLength + 10)); // 10 for stdcall mangling LPSTR szProbedEntrypointName = ((LPSTR)_alloca(dstbufsize + 1)); diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp index 0589cb2..208c734 100644 --- a/src/vm/methodtable.cpp +++ b/src/vm/methodtable.cpp @@ -10159,6 +10159,54 @@ static BOOL ComputeIsLayoutFixedInCurrentVersionBubble(MethodTable * pMT) return TRUE; } +static BOOL ComputeIsLayoutInCurrentVersionBubble(MethodTable* pMT) +{ + if (pMT->IsTruePrimitive() || pMT->IsEnum()) + return TRUE; + + if (!pMT->GetModule()->IsInCurrentVersionBubble()) + return FALSE; + + ApproxFieldDescIterator fieldIterator(pMT, ApproxFieldDescIterator::INSTANCE_FIELDS); + for (FieldDesc *pFD = fieldIterator.Next(); pFD != NULL; pFD = fieldIterator.Next()) + { + MethodTable * pFieldMT = pFD->GetApproxFieldTypeHandleThrowing().AsMethodTable(); + if (!pFieldMT->IsLayoutInCurrentVersionBubble()) + return FALSE; + } + + if (!pMT->IsValueType()) + { + pMT = pMT->GetParentMethodTable(); + + while ((pMT != g_pObjectClass) && (pMT != NULL)) + { + if (!pMT->IsLayoutInCurrentVersionBubble()) + return FALSE; + + pMT = pMT->GetParentMethodTable(); + } + } + + return TRUE; +} + +BOOL MethodTable::IsLayoutInCurrentVersionBubble() +{ + STANDARD_VM_CONTRACT; + + const MethodTableWriteableData * pWriteableData = GetWriteableData(); + if (!(pWriteableData->m_dwFlags & MethodTableWriteableData::enum_flag_NGEN_IsLayoutInCurrentVersionBubbleComputed)) + { + MethodTableWriteableData * pWriteableDataForWrite = GetWriteableDataForWrite(); + if (ComputeIsLayoutInCurrentVersionBubble(this)) + *EnsureWritablePages(&pWriteableDataForWrite->m_dwFlags) |= MethodTableWriteableData::enum_flag_NGEN_IsLayoutInCurrentVersionBubble; + *EnsureWritablePages(&pWriteableDataForWrite->m_dwFlags) |= MethodTableWriteableData::enum_flag_NGEN_IsLayoutInCurrentVersionBubbleComputed; + } + + return (pWriteableData->m_dwFlags & MethodTableWriteableData::enum_flag_NGEN_IsLayoutInCurrentVersionBubble) != 0; +} + // // Is field layout in this type fixed within the current version bubble? // This check does not take the inheritance chain into account. diff --git a/src/vm/methodtable.h b/src/vm/methodtable.h index 84f8399..74febeb 100644 --- a/src/vm/methodtable.h +++ b/src/vm/methodtable.h @@ -326,8 +326,10 @@ struct MethodTableWriteableData enum_flag_NGEN_OverridingInterface = 0x00080000, // Overriding interface that we should generate WinRT CCW stubs for. #ifdef FEATURE_READYTORUN_COMPILER - enum_flag_NGEN_IsLayoutFixedComputed = 0x0010000, // Set if we have cached the result of IsLayoutFixed computation - enum_flag_NGEN_IsLayoutFixed = 0x0020000, // The result of the IsLayoutFixed computation + enum_flag_NGEN_IsLayoutFixedComputed = 0x0010000, // Set if we have cached the result of IsLayoutFixed computation + enum_flag_NGEN_IsLayoutFixed = 0x0020000, // The result of the IsLayoutFixed computation + enum_flag_NGEN_IsLayoutInCurrentVersionBubbleComputed = 0x0040000, // Set if we have cached the result of IsLayoutInCurrentVersionBubble computation + enum_flag_NGEN_IsLayoutInCurrentVersionBubble = 0x0080000, // The result of the IsLayoutInCurrentVersionBubble computation #endif #endif // FEATURE_PREJIT @@ -4144,6 +4146,10 @@ public: #ifdef FEATURE_READYTORUN_COMPILER // + // Is field layout in this type within the current version bubble? + // + BOOL IsLayoutInCurrentVersionBubble(); + // // Is field layout in this type fixed within the current version bubble? // This check does not take the inheritance chain into account. // diff --git a/src/vm/zapsig.cpp b/src/vm/zapsig.cpp index dbb318d..4db83ec 100644 --- a/src/vm/zapsig.cpp +++ b/src/vm/zapsig.cpp @@ -1132,14 +1132,21 @@ BOOL ZapSig::EncodeMethod( // FUTURE: This condition should likely be changed or reevaluated once support for smaller version bubbles is implemented. if (IsReadyToRunCompilation() && (!IsLargeVersionBubbleEnabled() || !pMethod->GetModule()->IsInCurrentVersionBubble())) { - if (pResolvedToken == NULL) + if (pMethod->IsNDirect()) { - _ASSERTE(!"CORINFO_RESOLVED_TOKEN required to encode method!"); - ThrowHR(E_FAIL); + ownerType = pMethod->GetMethodTable_NoLogging(); } + else + { + if (pResolvedToken == NULL) + { + _ASSERTE(!"CORINFO_RESOLVED_TOKEN required to encode method!"); + ThrowHR(E_FAIL); + } - // Encode the referencing method type - ownerType = TypeHandle(pResolvedToken->hClass); + // Encode the referencing method type + ownerType = TypeHandle(pResolvedToken->hClass); + } } else #endif @@ -1198,7 +1205,9 @@ BOOL ZapSig::EncodeMethod( methodFlags |= ENCODE_METHOD_SIG_Constrained; } - Module * pReferencingModule = (Module *)pResolvedToken->tokenScope; + Module * pReferencingModule = pMethod->IsNDirect() ? + pMethod->GetModule() : + (Module *)pResolvedToken->tokenScope; if (!pReferencingModule->IsInCurrentVersionBubble()) { @@ -1208,7 +1217,9 @@ BOOL ZapSig::EncodeMethod( ThrowHR(E_FAIL); } - methodToken = pResolvedToken->token; + methodToken = pMethod->IsNDirect() ? + pMethod->GetMemberDef_NoLogging() : + pResolvedToken->token; if (TypeFromToken(methodToken) == mdtMethodSpec) { @@ -1218,7 +1229,7 @@ BOOL ZapSig::EncodeMethod( switch (TypeFromToken(methodToken)) { case mdtMethodDef: - _ASSERTE(pResolvedToken->pTypeSpec == NULL); + _ASSERTE(pMethod->IsNDirect() || pResolvedToken->pTypeSpec == NULL); if (!ownerType.HasInstantiation() || ownerType.IsTypicalTypeDefinition()) { methodFlags &= ~ENCODE_METHOD_SIG_OwnerType; @@ -1226,6 +1237,7 @@ BOOL ZapSig::EncodeMethod( break; case mdtMemberRef: + _ASSERTE(pResolvedToken != NULL); methodFlags |= ENCODE_METHOD_SIG_MemberRefToken; if (pResolvedToken->pTypeSpec == NULL) diff --git a/src/zap/zapinfo.cpp b/src/zap/zapinfo.cpp index 560400b..1e49846 100644 --- a/src/zap/zapinfo.cpp +++ b/src/zap/zapinfo.cpp @@ -205,8 +205,14 @@ CORJIT_FLAGS ZapInfo::ComputeJitFlags(CORINFO_METHOD_HANDLE handle) #ifdef FEATURE_READYTORUN_COMPILER if (IsReadyToRunCompilation()) + { jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_READYTORUN); +#ifndef PLATFORM_UNIX + // PInvoke Helpers are not yet implemented on non-Windows platforms + jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_PINVOKE_HELPERS); #endif + } +#endif // FEATURE_READYTORUN_COMPILER return jitFlags; } @@ -1988,12 +1994,15 @@ void * ZapInfo::getAddressOfPInvokeFixup(CORINFO_METHOD_HANDLE method,void **ppI m_pImage->m_pPreloader->AddMethodToTransitiveClosureOfInstantiations(method); - CORINFO_MODULE_HANDLE moduleHandle = m_pEECompileInfo->GetLoaderModuleForEmbeddableMethod(method); - if (moduleHandle == m_pImage->m_hModule - && m_pImage->m_pPreloader->CanEmbedMethodHandle(method, m_currentMethodHandle)) + if (!IsReadyToRunCompilation()) { - *ppIndirection = NULL; - return PVOID(m_pImage->GetWrappers()->GetAddrOfPInvokeFixup(method)); + CORINFO_MODULE_HANDLE moduleHandle = m_pEECompileInfo->GetLoaderModuleForEmbeddableMethod(method); + if (moduleHandle == m_pImage->m_hModule + && m_pImage->m_pPreloader->CanEmbedMethodHandle(method, m_currentMethodHandle)) + { + *ppIndirection = NULL; + return PVOID(m_pImage->GetWrappers()->GetAddrOfPInvokeFixup(method)); + } } // @@ -3849,9 +3858,11 @@ CorInfoUnmanagedCallConv ZapInfo::getUnmanagedCallConv(CORINFO_METHOD_HANDLE met BOOL ZapInfo::pInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig) { - // READYTORUN: FUTURE: P/Invoke +#ifdef PLATFORM_UNIX + // TODO: Support for pinvoke helpers on non-Windows platforms if (IsReadyToRunCompilation()) - return TRUE; + return TRUE; +#endif return m_pEEJitInfo->pInvokeMarshalingRequired(method, sig); } diff --git a/src/zap/zapreadytorun.cpp b/src/zap/zapreadytorun.cpp index f0260a5..8d83ff7 100644 --- a/src/zap/zapreadytorun.cpp +++ b/src/zap/zapreadytorun.cpp @@ -549,6 +549,8 @@ static_assert_no_msg((int)READYTORUN_FIXUP_DelegateCtor == (int)ENC static_assert_no_msg((int)READYTORUN_FIXUP_DeclaringTypeHandle == (int)ENCODE_DECLARINGTYPE_HANDLE); +static_assert_no_msg((int)READYTORUN_FIXUP_IndirectPInvokeTarget == (int)ENCODE_INDIRECT_PINVOKE_TARGET); + // // READYTORUN_EXCEPTION // -- 2.7.4