diff options
30 files changed, 739 insertions, 724 deletions
diff --git a/clrdefinitions.cmake b/clrdefinitions.cmake index 5ff9be4647..20040b75a9 100644 --- a/clrdefinitions.cmake +++ b/clrdefinitions.cmake @@ -27,6 +27,7 @@ elseif (CLR_CMAKE_TARGET_ARCH_ARM64) add_definitions(-DDBG_TARGET_64BIT=1) add_definitions(-DDBG_TARGET_ARM64=1) add_definitions(-DDBG_TARGET_WIN64=1) + add_definitions(-DFEATURE_MULTIREG_RETURN) elseif (CLR_CMAKE_TARGET_ARCH_ARM) if (CLR_CMAKE_PLATFORM_UNIX) add_definitions(-DDBG_TARGET_ARM_UNIX) @@ -166,6 +167,7 @@ add_definitions(-DFEATURE_SVR_GC) add_definitions(-DFEATURE_SYMDIFF) add_definitions(-DFEATURE_SYNTHETIC_CULTURES) if(CLR_CMAKE_PLATFORM_UNIX_AMD64) + add_definitions(-DFEATURE_MULTIREG_RETURN) add_definitions(-DFEATURE_UNIX_AMD64_STRUCT_PASSING) add_definitions(-DFEATURE_UNIX_AMD64_STRUCT_PASSING_ITF) endif (CLR_CMAKE_PLATFORM_UNIX_AMD64) diff --git a/src/debug/daccess/nidump.cpp b/src/debug/daccess/nidump.cpp index d151a54212..32eab498d3 100644 --- a/src/debug/daccess/nidump.cpp +++ b/src/debug/daccess/nidump.cpp @@ -3112,7 +3112,7 @@ void NativeImageDumper::DumpCompleteMethod(PTR_Module module, MethodIterator& mi GCDump gcDump(gcInfoToken.Version); gcDump.gcPrintf = stringOutFn; #if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER) - GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_CODE_LENGTH, 0); + GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_CODE_LENGTH); methodSize = gcInfoDecoder.GetCodeLength(); #endif @@ -9440,7 +9440,9 @@ void NativeImageDumper::DumpReadyToRunMethod(PCODE pEntryPoint, PTR_RUNTIME_FUNC GCDump gcDump(GCINFO_VERSION); gcDump.gcPrintf = stringOutFn; #if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER) - GcInfoDecoder gcInfoDecoder({ curGCInfoPtr, GCINFO_VERSION }, DECODE_CODE_LENGTH, 0); + UINT32 r2rversion = m_pReadyToRunHeader->MajorVersion; + UINT32 gcInfoVersion = GCInfoToken::ReadyToRunVersionToGcInfoVersion(r2rversion); + GcInfoDecoder gcInfoDecoder({ curGCInfoPtr, gcInfoVersion }, DECODE_CODE_LENGTH); methodSize = gcInfoDecoder.GetCodeLength(); #endif diff --git a/src/gcdump/gcdumpnonx86.cpp b/src/gcdump/gcdumpnonx86.cpp index 53e16ffbff..7343ac9771 100644 --- a/src/gcdump/gcdumpnonx86.cpp +++ b/src/gcdump/gcdumpnonx86.cpp @@ -284,7 +284,8 @@ size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock, | DECODE_VARARG | DECODE_GENERICS_INST_CONTEXT | DECODE_GC_LIFETIMES - | DECODE_PROLOG_LENGTH), + | DECODE_PROLOG_LENGTH + | DECODE_RETURN_KIND), 0); if (NO_SECURITY_OBJECT != hdrdecoder.GetSecurityObjectStackSlot() || @@ -438,6 +439,9 @@ size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock, gcPrintf("Size of parameter area: %x\n", hdrdecoder.GetSizeOfStackParameterArea()); #endif + ReturnKind returnKind = hdrdecoder.GetReturnKind(); + gcPrintf("Return Kind: %s\n", ReturnKindToString(returnKind)); + UINT32 cbEncodedMethodSize = hdrdecoder.GetCodeLength(); gcPrintf("Code size: %x\n", cbEncodedMethodSize); diff --git a/src/gcinfo/gcinfoencoder.cpp b/src/gcinfo/gcinfoencoder.cpp index 514a3c96be..3e587ea5ef 100644 --- a/src/gcinfo/gcinfoencoder.cpp +++ b/src/gcinfo/gcinfoencoder.cpp @@ -324,13 +324,17 @@ typedef SimplerHashTable<const BitArray *, LiveStateFuncs, UINT32, GcInfoHashBeh // Pi = partially-interruptible; methods with zero fully-interruptible ranges GcInfoSize g_FiGcInfoSize; GcInfoSize g_PiGcInfoSize; +// Number of methods with GcInfo that have SlimHeader +size_t g_NumSlimHeaders = 0; +// Number of methods with GcInfo that have FatHeader +size_t g_NumFatHeaders = 0; GcInfoSize::GcInfoSize() { memset(this, 0, sizeof(*this)); } -GcInfoSize& operator+=(const GcInfoSize& other) +GcInfoSize& GcInfoSize::operator+=(const GcInfoSize& other) { TotalSize += other.TotalSize; @@ -351,7 +355,7 @@ GcInfoSize& operator+=(const GcInfoSize& other) GenericsCtxSize += other.GenericsCtxSize; PspSymSize += other.PspSymSize; StackBaseSize += other.StackBaseSize; - FrameMarkerSize += other.FrameMarkerSize; + ReversePInvokeFrameSize += other.ReversePInvokeFrameSize; FixedAreaSize += other.FixedAreaSize; NumCallSitesSize += other.NumCallSitesSize; NumRangesSize += other.NumRangesSize; @@ -398,8 +402,9 @@ void GcInfoSize::Log(DWORD level, const char * header) LogSpew(LF_GCINFO, level, "GsCookie: %Iu\n", GsCookieSize); LogSpew(LF_GCINFO, level, "PspSym: %Iu\n", PspSymSize); LogSpew(LF_GCINFO, level, "GenericsCtx: %Iu\n", GenericsCtxSize); - LogSpew(LF_GCINFO, level, "FrameMarker: %Iu\n", FrameMarkerSize); + LogSpew(LF_GCINFO, level, "StackBase: %Iu\n", StackBaseSize); LogSpew(LF_GCINFO, level, "FixedArea: %Iu\n", FixedAreaSize); + LogSpew(LF_GCINFO, level, "ReversePInvokeFrame: %Iu\n", ReversePInvokeFrameSize); LogSpew(LF_GCINFO, level, "NumCallSites: %Iu\n", NumCallSitesSize); LogSpew(LF_GCINFO, level, "NumRanges: %Iu\n", NumRangesSize); LogSpew(LF_GCINFO, level, "CallSiteOffsets: %Iu\n", CallSitePosSize); @@ -488,17 +493,27 @@ GcInfoEncoder::GcInfoEncoder( m_StackBaseRegister = NO_STACK_BASE_REGISTER; m_SizeOfEditAndContinuePreservedArea = NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA; + m_ReversePInvokeFrameSlot = NO_REVERSE_PINVOKE_FRAME; m_WantsReportOnlyLeaf = false; m_IsVarArg = false; m_pLastInterruptibleRange = NULL; #ifdef _DEBUG m_IsSlotTableFrozen = FALSE; +#endif //_DEBUG + +#ifndef _TARGET_X86_ + // If the compiler doesn't set the GCInfo, report RT_Unset. + // This is used for compatibility with JITs that aren't updated to use the new API. + m_ReturnKind = RT_Unset; +#else + m_ReturnKind = RT_Illegal; +#endif // _TARGET_X86_ m_CodeLength = 0; #ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA m_SizeOfStackOutgoingAndScratchArea = -1; #endif // FIXED_STACK_PARAMETER_SCRATCH_AREA -#endif //_DEBUG + } #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED @@ -758,6 +773,17 @@ void GcInfoEncoder::SetSizeOfStackOutgoingAndScratchArea( UINT32 size ) } #endif // FIXED_STACK_PARAMETER_SCRATCH_AREA +void GcInfoEncoder::SetReversePInvokeFrameSlot(INT32 spOffset) +{ + m_ReversePInvokeFrameSlot = spOffset; +} + +void GcInfoEncoder::SetReturnKind(ReturnKind returnKind) +{ + _ASSERTE(IsValidReturnKind(returnKind)); + + m_ReturnKind = returnKind; +} struct GcSlotDescAndId { @@ -765,7 +791,6 @@ struct GcSlotDescAndId UINT32 m_SlotId; }; - int __cdecl CompareSlotDescAndIdBySlotDesc(const void* p1, const void* p2) { const GcSlotDesc* pFirst = &reinterpret_cast<const GcSlotDescAndId*>(p1)->m_SlotDesc; @@ -985,20 +1010,26 @@ void GcInfoEncoder::Build() // Method header /////////////////////////////////////////////////////////////////////// + UINT32 hasSecurityObject = (m_SecurityObjectStackSlot != NO_SECURITY_OBJECT); UINT32 hasGSCookie = (m_GSCookieStackSlot != NO_GS_COOKIE); UINT32 hasContextParamType = (m_GenericsInstContextStackSlot != NO_GENERICS_INST_CONTEXT); + UINT32 hasReversePInvokeFrame = (m_ReversePInvokeFrameSlot != NO_REVERSE_PINVOKE_FRAME); BOOL slimHeader = (!m_IsVarArg && !hasSecurityObject && !hasGSCookie && (m_PSPSymStackSlot == NO_PSP_SYM) && - !hasContextParamType && !m_WantsReportOnlyLeaf && (m_InterruptibleRanges.Count() == 0) && + !hasContextParamType && !m_WantsReportOnlyLeaf && (m_InterruptibleRanges.Count() == 0) && !hasReversePInvokeFrame && ((m_StackBaseRegister == NO_STACK_BASE_REGISTER) || (NORMALIZE_STACK_BASE_REGISTER(m_StackBaseRegister) == 0))) && (m_SizeOfEditAndContinuePreservedArea == NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA); + // All new code is generated for the latest GCINFO_VERSION. + // So, always encode RetunrKind and encode ReversePInvokeFrameSlot where applicable. if (slimHeader) { // Slim encoding means nothing special, partially interruptible, maybe a default frame register GCINFO_WRITE(m_Info1, 0, 1, FlagsSize); // Slim encoding GCINFO_WRITE(m_Info1, (m_StackBaseRegister == NO_STACK_BASE_REGISTER) ? 0 : 1, 1, FlagsSize); + + GCINFO_WRITE(m_Info1, m_ReturnKind, SIZE_OF_RETURN_KIND_IN_SLIM_HEADER, RetKindSize); } else { @@ -1011,6 +1042,9 @@ void GcInfoEncoder::Build() GCINFO_WRITE(m_Info1, ((m_StackBaseRegister != NO_STACK_BASE_REGISTER) ? 1 : 0), 1, FlagsSize); GCINFO_WRITE(m_Info1, (m_WantsReportOnlyLeaf ? 1 : 0), 1, FlagsSize); GCINFO_WRITE(m_Info1, ((m_SizeOfEditAndContinuePreservedArea != NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA) ? 1 : 0), 1, FlagsSize); + GCINFO_WRITE(m_Info1, (hasReversePInvokeFrame ? 1 : 0), 1, FlagsSize); + + GCINFO_WRITE(m_Info1, m_ReturnKind, SIZE_OF_RETURN_KIND_IN_FAT_HEADER, RetKindSize); } _ASSERTE( m_CodeLength > 0 ); @@ -1109,6 +1143,12 @@ void GcInfoEncoder::Build() GCINFO_WRITE_VARL_U(m_Info1, m_SizeOfEditAndContinuePreservedArea, SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE, EncPreservedSlots); } + if (hasReversePInvokeFrame) + { + _ASSERTE(!slimHeader); + GCINFO_WRITE_VARL_S(m_Info1, NORMALIZE_STACK_SLOT(m_ReversePInvokeFrameSlot), REVERSE_PINVOKE_FRAME_ENCBASE, ReversePInvokeFrameSize); + } + #ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA if (!slimHeader) { @@ -2305,6 +2345,15 @@ lExitSuccess:; //------------------------------------------------------------------- #ifdef MEASURE_GCINFO + if (slimHeader) + { + g_NumSlimHeaders++; + } + else + { + g_NumFatHeaders++; + } + m_CurrentMethodSize.NumMethods = 1; #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED m_CurrentMethodSize.NumCallSites = m_NumCallSites; @@ -2331,6 +2380,8 @@ lExitSuccess:; m_CurrentMethodSize.Log(LL_INFO100, "=== PartiallyInterruptible method breakdown ===\r\n"); g_PiGcInfoSize.Log(LL_INFO10, "=== PartiallyInterruptible global breakdown ===\r\n"); } + LogSpew(LF_GCINFO, LL_INFO10, "Total SlimHeaders: %Iu\n", g_NumSlimHeaders); + LogSpew(LF_GCINFO, LL_INFO10, "NumMethods: %Iu\n", g_NumFatHeaders); #endif } diff --git a/src/inc/gcinfo.h b/src/inc/gcinfo.h index 500e1b7a02..8d249a38a6 100644 --- a/src/inc/gcinfo.h +++ b/src/inc/gcinfo.h @@ -32,8 +32,15 @@ const unsigned this_OFFSET_FLAG = 0x2; // the offset is "this" // The current GCInfo Version //----------------------------------------------------------------------------- +#ifdef _TARGET_X86_ +// X86 GcInfo encoding is yet to be changed. #define GCINFO_VERSION 1 +#else +#define GCINFO_VERSION 2 +#endif // _TARGET_X86_ +#define MIN_GCINFO_VERSION_WITH_RETURN_KIND 2 +#define MIN_GCINFO_VERSION_WITH_REV_PINVOKE_FRAME 2 //----------------------------------------------------------------------------- // GCInfoToken: A wrapper that contains the GcInfo data and version number. // @@ -45,14 +52,29 @@ const unsigned this_OFFSET_FLAG = 0x2; // the offset is "this" // 1) The current GCINFO_VERSION for JITted and Ngened images // 2) A function of the Ready - to - run major version stored in READYTORUN_HEADER // for ready - to - run images.ReadyToRunJitManager::JitTokenToGCInfoVersion() -// provides the GcInfo version for any Method.Currently, there's only one -// version of GCInfo. +// provides the GcInfo version for any Method. //----------------------------------------------------------------------------- struct GCInfoToken { PTR_VOID Info; UINT32 Version; + + BOOL IsReturnKindAvailable() + { + return (Version >= MIN_GCINFO_VERSION_WITH_RETURN_KIND); + } + BOOL IsReversePInvokeFrameAvailable() + { + return (Version >= MIN_GCINFO_VERSION_WITH_REV_PINVOKE_FRAME); + } + + static UINT32 ReadyToRunVersionToGcInfoVersion(UINT32 readyToRunMajorVersion) + { + // GcInfo version is 1 up to ReadyTorun version 1.x + // GcInfo version is current from ReadyToRun version 2.0 + return (readyToRunMajorVersion == 1) ? 1 : GCINFO_VERSION; + } }; /*****************************************************************************/ diff --git a/src/inc/gcinfodecoder.h b/src/inc/gcinfodecoder.h index f703727a76..c77c3598b0 100644 --- a/src/inc/gcinfodecoder.h +++ b/src/inc/gcinfodecoder.h @@ -161,6 +161,7 @@ enum ICodeManagerFlags enum GcInfoDecoderFlags { + DECODE_EVERYTHING = 0x0, DECODE_SECURITY_OBJECT = 0x01, // stack location of security object DECODE_CODE_LENGTH = 0x02, DECODE_VARARG = 0x04, @@ -174,6 +175,8 @@ enum GcInfoDecoderFlags DECODE_FOR_RANGES_CALLBACK = 0x200, DECODE_PROLOG_LENGTH = 0x400, // length of the prolog (used to avoid reporting generics context) DECODE_EDIT_AND_CONTINUE = 0x800, + DECODE_REVERSE_PINVOKE_VAR = 0x1000, + DECODE_RETURN_KIND = 0x2000 }; enum GcInfoHeaderFlags @@ -190,11 +193,12 @@ enum GcInfoHeaderFlags GC_INFO_HAS_STACK_BASE_REGISTER = 0x40, GC_INFO_WANTS_REPORT_ONLY_LEAF = 0x80, GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS = 0x100, + GC_INFO_REVERSE_PINVOKE_FRAME = 0x200, - GC_INFO_FLAGS_BIT_SIZE = 9, + GC_INFO_FLAGS_BIT_SIZE_VERSION_1 = 9, + GC_INFO_FLAGS_BIT_SIZE = 10, }; - class BitStreamReader { public: @@ -430,7 +434,7 @@ public: // If you are not insterested in interruptibility or gc lifetime information, pass 0 as instructionOffset GcInfoDecoder( GCInfoToken gcInfoToken, - GcInfoDecoderFlags flags, + GcInfoDecoderFlags flags = DECODE_EVERYTHING, UINT32 instructionOffset = 0 ); @@ -486,10 +490,12 @@ public: UINT32 GetPrologSize(); INT32 GetPSPSymStackSlot(); INT32 GetGenericsInstContextStackSlot(); + INT32 GetReversePInvokeStackSlot(); bool HasMethodDescGenericsInstContext(); bool HasMethodTableGenericsInstContext(); bool GetIsVarArg(); bool WantsReportOnlyLeaf(); + ReturnKind GetReturnKind(); UINT32 GetCodeLength(); UINT32 GetStackBaseRegister(); UINT32 GetSizeOfEditAndContinuePreservedArea(); @@ -512,6 +518,7 @@ private: bool m_WantsReportOnlyLeaf; INT32 m_SecurityObjectStackSlot; INT32 m_GSCookieStackSlot; + INT32 m_ReversePInvokeStackSlot; UINT32 m_ValidRangeStart; UINT32 m_ValidRangeEnd; INT32 m_PSPSymStackSlot; @@ -519,6 +526,8 @@ private: UINT32 m_CodeLength; UINT32 m_StackBaseRegister; UINT32 m_SizeOfEditAndContinuePreservedArea; + INT32 m_ReversePInvokeFrameSlot; + ReturnKind m_ReturnKind; #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED UINT32 m_NumSafePoints; UINT32 m_SafePointIndex; @@ -533,8 +542,8 @@ private: #ifdef _DEBUG GcInfoDecoderFlags m_Flags; PTR_CBYTE m_GcInfoAddress; - UINT32 m_Version; #endif + UINT32 m_Version; static bool SetIsInterruptibleCB (UINT32 startOffset, UINT32 stopOffset, LPVOID hCallback); diff --git a/src/inc/gcinfoencoder.h b/src/inc/gcinfoencoder.h index 8875d3b492..838f1babf7 100644 --- a/src/inc/gcinfoencoder.h +++ b/src/inc/gcinfoencoder.h @@ -12,6 +12,15 @@ ENCODING LAYOUT 1. Header + + Slim Header for simple and common cases: + - EncodingType[Slim] + - ReturnKind (Fat: 2 bits) + - CodeLength + - NumCallSites (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED) + + Fat Header for other cases: + - EncodingType[Fat] - Flag: isVarArg, hasSecurityObject, hasGSCookie, @@ -20,6 +29,8 @@ hasStackBaseregister, wantsReportOnlyLeaf, hasSizeOfEditAndContinuePreservedArea + hasReversePInvokeFrame, + - ReturnKind (Fat: 4 bits) - CodeLength - Prolog (if hasSecurityObject || hasGenericsInstContextStackSlot || hasGSCookie) - Epilog (if hasGSCookie) @@ -29,9 +40,11 @@ - GenericsInstContextStackSlot (if any) - StackBaseRegister (if any) - SizeOfEditAndContinuePreservedArea (if any) + - ReversePInvokeFrameSlot (if any) - SizeOfStackOutgoingAndScratchArea (#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA) - NumCallSites (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED) - NumInterruptibleRanges + 2. Call sites offsets (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED) 3. Fully-interruptible ranges 4. Slot table @@ -102,14 +115,15 @@ struct GcInfoSize size_t SizeOfCode; size_t FlagsSize; + size_t RetKindSize; size_t CodeLengthSize; size_t ProEpilogSize; size_t SecObjSize; size_t GsCookieSize; - size_t GenericsCtxSize; size_t PspSymSize; + size_t GenericsCtxSize; size_t StackBaseSize; - size_t FrameMarkerSize; + size_t ReversePInvokeFrameSize; size_t FixedAreaSize; size_t NumCallSitesSize; size_t NumRangesSize; @@ -129,7 +143,7 @@ struct GcInfoSize size_t ChunkTransitionSize; GcInfoSize(); - GcInfoSize& operator+=(GcInfoSize& other); + GcInfoSize& operator+=(const GcInfoSize& other); void Log(DWORD level, const char * header); }; #endif @@ -390,6 +404,11 @@ public: ); + //------------------------------------------------------------------------ + // ReturnKind + //------------------------------------------------------------------------ + + void SetReturnKind(ReturnKind returnKind); //------------------------------------------------------------------------ // Miscellaneous method information @@ -400,6 +419,7 @@ public: void SetGSCookieStackSlot( INT32 spOffsetGSCookie, UINT32 validRangeStart, UINT32 validRangeEnd ); void SetPSPSymStackSlot( INT32 spOffsetPSPSym ); void SetGenericsInstContextStackSlot( INT32 spOffsetGenericsContext, GENERIC_CONTEXTPARAM_TYPE type); + void SetReversePInvokeFrameSlot(INT32 spOffset); void SetIsVarArg(); void SetCodeLength( UINT32 length ); @@ -472,9 +492,11 @@ private: INT32 m_PSPSymStackSlot; INT32 m_GenericsInstContextStackSlot; GENERIC_CONTEXTPARAM_TYPE m_contextParamType; + ReturnKind m_ReturnKind; UINT32 m_CodeLength; UINT32 m_StackBaseRegister; UINT32 m_SizeOfEditAndContinuePreservedArea; + INT32 m_ReversePInvokeFrameSlot; InterruptibleRange* m_pLastInterruptibleRange; #ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA diff --git a/src/inc/gcinfotypes.h b/src/inc/gcinfotypes.h index fc624b2c0a..c08dd79c16 100644 --- a/src/inc/gcinfotypes.h +++ b/src/inc/gcinfotypes.h @@ -46,7 +46,6 @@ #define DISABLE_EH_VECTORS #endif - #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) #define FIXED_STACK_PARAMETER_SCRATCH_AREA #endif @@ -136,6 +135,210 @@ struct GcStackSlot } }; +//-------------------------------------------------------------------------------- +// ReturnKind -- encoding return type information in GcInfo +// +// When a method is stopped at a call - site for GC (ex: via return-address +// hijacking) the runtime needs to know whether the value is a GC - value +// (gc - pointer or gc - pointers stored in an aggregate). +// It needs this information so that mark - phase can preserve the gc-pointers +// being returned. +// +// The Runtime doesn't need the precise return-type of a method. +// It only needs to find the GC-pointers in the return value. +// The only scenarios currently supported by CoreCLR are: +// 1. Object references +// 2. ByRef pointers +// 3. ARM64/X64 only : Structs returned in two registers +// 4. X86 only : Floating point returns to perform the correct save/restore +// of the return value around return-hijacking. +// +// Based on these cases, the legal set of ReturnKind enumerations are specified +// for each architecture/encoding. +// A value of this enumeration is stored in the GcInfo header. +// +//-------------------------------------------------------------------------------- + +// RT_Unset: An intermediate step for staged bringup. +// When ReturnKind is RT_Unset, it means that the JIT did not set +// the ReturnKind in the GCInfo, and therefore the VM cannot rely on it, +// and must use other mechanisms (similar to GcInfo ver 1) to determine +// the Return type's GC information. +// +// RT_Unset is only used in the following situations: +// X64: Used by JIT64 until updated to use GcInfo v2 API +// ARM: Used by JIT32 until updated to use GcInfo v2 API +// +// RT_Unset should have a valid encoding, whose bits are actually stored in the image. +// For X86, there are no free bits, and there's no RT_Unused enumeration. + +#if defined(_TARGET_X86_) + +// 00 RT_Scalar +// 01 RT_Object +// 10 RT_ByRef +// 11 RT_Float + +#elif defined(_TARGET_ARM_) + +// 00 RT_Scalar +// 01 RT_Object +// 10 RT_ByRef +// 11 RT_Unset + +#elif defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) + +// Slim Header: + +// 00 RT_Scalar +// 01 RT_Object +// 10 RT_ByRef +// 11 RT_Unset + +// Fat Header: + +// 0000 RT_Scalar +// 0001 RT_Object +// 0010 RT_ByRef +// 0011 RT_Unset +// 0100 RT_Scalar_Obj +// 1000 RT_Scalar_ByRef +// 0101 RT_Obj_Obj +// 1001 RT_Obj_ByRef +// 0110 RT_ByRef_Obj +// 1010 RT_ByRef_ByRef + +#else +#ifdef PORTABILITY_WARNING +PORTABILITY_WARNING("Need ReturnKind for new Platform") +#endif // PORTABILITY_WARNING +#endif // Target checks + +enum ReturnKind { + + // Cases for Return in one register + + RT_Scalar = 0, + RT_Object = 1, + RT_ByRef = 2, + +#ifdef _TARGET_X86_ + RT_Float = 3, // Encoding 3 means RT_Float on X86 +#else + RT_Unset = 3, // RT_Unset on other platforms +#endif // _TARGET_X86_ + + // Cases for Struct Return in two registers + // + // We have the following equivalencies, because the VM's behavior is the same + // for both cases: + // RT_Scalar_Scalar == RT_Scalar + // RT_Obj_Scalar == RT_Object + // RT_ByRef_Scalar == RT_Byref + // The encoding for these equivalencies will play out well because + // RT_Scalar is zero. + // + // Naming: RT_firstReg_secondReg + // Encoding: <Two bits for secondRef> <Two bits for first Reg> + // + // This encoding with exclusive bits for each register is chosen for ease of use, + // and because it doesn't cost any more bits. + // It can be changed (ex: to a linear sequence) if necessary. + // For example, we can encode the GC-information for the two registers in 3 bits (instead of 4) + // if we approximate RT_Obj_ByRef and RT_ByRef_Obj as RT_ByRef_ByRef. + + // RT_Scalar_Scalar = RT_Scalar + RT_Scalar_Obj = RT_Object << 2 | RT_Scalar, + RT_Scalar_ByRef = RT_ByRef << 2 | RT_Scalar, + + // RT_Obj_Scalar = RT_Object + RT_Obj_Obj = RT_Object << 2 | RT_Object, + RT_Obj_ByRef = RT_ByRef << 2 | RT_Object, + + // RT_ByRef_Scalar = RT_Byref + RT_ByRef_Obj = RT_Object << 2 | RT_ByRef, + RT_ByRef_ByRef = RT_ByRef << 2 | RT_ByRef, + + // Illegal or uninitialized value, + // Not a valid encoding, never written to image. + RT_Illegal = 0xFF +}; + +// Identify ReturnKinds containing useful information +inline bool IsValidReturnKind(ReturnKind returnKind) +{ + return (returnKind != RT_Illegal) +#ifndef _TARGET_X86_ + && (returnKind != RT_Unset) +#endif // _TARGET_X86_ + ; +} + +// Identify ReturnKinds that can be a part of a multi-reg struct return +inline bool IsValidFieldReturnKind(ReturnKind returnKind) +{ + return (returnKind == RT_Scalar || returnKind == RT_Object || returnKind == RT_ByRef); +} + +inline bool IsValidReturnRegister(size_t regNo) +{ + return (regNo == 0) +#ifdef FEATURE_MULTIREG_RETURN + || (regNo == 1) +#endif // FEATURE_MULTIREG_RETURN + ; +} + +// Helpers for combining/extracting individual ReturnKinds from/to Struct ReturnKinds. +// Encoding is two bits per register + +inline ReturnKind GetStructReturnKind(ReturnKind reg0, ReturnKind reg1) +{ + _ASSERTE(IsValidFieldReturnKind(reg0) && IsValidFieldReturnKind(reg1)); + + ReturnKind structReturnKind = (ReturnKind)(reg1 << 2 | reg0); + + _ASSERTE(IsValidReturnKind(structReturnKind)); + + return structReturnKind; +} + +inline ReturnKind ExtractRegReturnKind(ReturnKind returnKind, size_t regNo) +{ + _ASSERTE(IsValidReturnKind(returnKind)); + _ASSERTE(IsValidReturnRegister(regNo)); + + ReturnKind regReturnKind = (ReturnKind)((returnKind >> (regNo * 2)) & 3); + + _ASSERTE(IsValidReturnKind(regReturnKind)); + _ASSERTE((regNo == 0) || IsValidFieldReturnKind(regReturnKind)); + + return regReturnKind; +} + +inline const char *ReturnKindToString(ReturnKind returnKind) +{ + switch (returnKind) { + case RT_Scalar: return "Scalar"; + case RT_Object: return "Object"; + case RT_ByRef: return "ByRef"; +#ifdef _TARGET_X86_ + case RT_Float: return "Float"; +#else + case RT_Unset: return "UNSET"; +#endif // _TARGET_X86_ + case RT_Scalar_Obj: return "{Scalar, Object}"; + case RT_Scalar_ByRef: return "{Scalar, ByRef}"; + case RT_Obj_Obj: return "{Object, Object}"; + case RT_Obj_ByRef: return "{Object, ByRef}"; + case RT_ByRef_Obj: return "{ByRef, Object}"; + case RT_ByRef_ByRef: return "{ByRef, ByRef}"; + + case RT_Illegal: return "<Illegal>"; + default: return "!Impossible!"; + } +} + #ifdef _TARGET_X86_ #include <stdlib.h> // For memcmp() @@ -375,6 +578,7 @@ void FASTCALL decodeCallPattern(int pattern, #define NO_STACK_BASE_REGISTER (0xffffffff) #define NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA (0xffffffff) #define NO_GENERICS_INST_CONTEXT (-1) +#define NO_REVERSE_PINVOKE_FRAME (-1) #define NO_PSP_SYM (-1) #if defined(_TARGET_AMD64_) @@ -408,9 +612,12 @@ void FASTCALL decodeCallPattern(int pattern, #define SECURITY_OBJECT_STACK_SLOT_ENCBASE 6 #define GS_COOKIE_STACK_SLOT_ENCBASE 6 #define CODE_LENGTH_ENCBASE 8 +#define SIZE_OF_RETURN_KIND_IN_SLIM_HEADER 2 +#define SIZE_OF_RETURN_KIND_IN_FAT_HEADER 4 #define STACK_BASE_REGISTER_ENCBASE 3 #define SIZE_OF_STACK_AREA_ENCBASE 3 #define SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE 4 +#define REVERSE_PINVOKE_FRAME_ENCBASE 6 #define NUM_REGISTERS_ENCBASE 2 #define NUM_STACK_SLOTS_ENCBASE 2 #define NUM_UNTRACKED_SLOTS_ENCBASE 1 @@ -463,9 +670,12 @@ void FASTCALL decodeCallPattern(int pattern, #define SECURITY_OBJECT_STACK_SLOT_ENCBASE 5 #define GS_COOKIE_STACK_SLOT_ENCBASE 5 #define CODE_LENGTH_ENCBASE 7 +#define SIZE_OF_RETURN_KIND_IN_SLIM_HEADER 2 +#define SIZE_OF_RETURN_KIND_IN_FAT_HEADER 2 #define STACK_BASE_REGISTER_ENCBASE 1 #define SIZE_OF_STACK_AREA_ENCBASE 3 #define SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE 3 +#define REVERSE_PINVOKE_FRAME_ENCBASE 5 #define NUM_REGISTERS_ENCBASE 2 #define NUM_STACK_SLOTS_ENCBASE 3 #define NUM_UNTRACKED_SLOTS_ENCBASE 3 @@ -515,9 +725,12 @@ void FASTCALL decodeCallPattern(int pattern, #define SECURITY_OBJECT_STACK_SLOT_ENCBASE 6 #define GS_COOKIE_STACK_SLOT_ENCBASE 6 #define CODE_LENGTH_ENCBASE 8 +#define SIZE_OF_RETURN_KIND_IN_SLIM_HEADER 2 +#define SIZE_OF_RETURN_KIND_IN_FAT_HEADER 4 #define STACK_BASE_REGISTER_ENCBASE 2 // FP encoded as 0, SP as 2. #define SIZE_OF_STACK_AREA_ENCBASE 3 #define SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE 4 +#define REVERSE_PINVOKE_FRAME_ENCBASE 6 #define NUM_REGISTERS_ENCBASE 3 #define NUM_STACK_SLOTS_ENCBASE 2 #define NUM_UNTRACKED_SLOTS_ENCBASE 1 @@ -573,9 +786,12 @@ PORTABILITY_WARNING("Please specialize these definitions for your platform!") #define SECURITY_OBJECT_STACK_SLOT_ENCBASE 6 #define GS_COOKIE_STACK_SLOT_ENCBASE 6 #define CODE_LENGTH_ENCBASE 6 +#define SIZE_OF_RETURN_KIND_IN_SLIM_HEADER 2 +#define SIZE_OF_RETURN_KIND_IN_FAT_HEADER 2 #define STACK_BASE_REGISTER_ENCBASE 3 #define SIZE_OF_STACK_AREA_ENCBASE 6 #define SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE 3 +#define REVERSE_PINVOKE_FRAME_ENCBASE 6 #define NUM_REGISTERS_ENCBASE 3 #define NUM_STACK_SLOTS_ENCBASE 5 #define NUM_UNTRACKED_SLOTS_ENCBASE 5 diff --git a/src/jit/gcencode.cpp b/src/jit/gcencode.cpp index ec0eba3fe3..1f9065e2a6 100644 --- a/src/jit/gcencode.cpp +++ b/src/jit/gcencode.cpp @@ -3398,6 +3398,15 @@ public: } } + void SetReturnKind(ReturnKind returnKind) + { + m_gcInfoEncoder->SetReturnKind(returnKind); + if (m_doLogging) + { + printf("Set ReturnKind to %s.\n", ReturnKindToString(returnKind)); + } + } + void SetStackBaseRegister(UINT32 registerNumber) { m_gcInfoEncoder->SetStackBaseRegister(registerNumber); @@ -3495,13 +3504,25 @@ public: #endif // DEBUG +ReturnKind GCTypeToReturnKind(CorInfoGCType gcType) +{ + + switch (gcType) { + case TYPE_GC_NONE: return RT_Scalar; + case TYPE_GC_REF: return RT_Object; + case TYPE_GC_BYREF: return RT_ByRef; + default: + _ASSERTE(!"TYP_GC_OTHER is unexpected"); + return RT_Illegal; + } +} void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSize, unsigned prologSize) { #ifdef DEBUG - if (compiler->verbose) + if (compiler->verbose) printf("*************** In gcInfoBlockHdrSave()\n"); #endif @@ -3511,11 +3532,46 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, gcInfoEncoderWithLog->SetCodeLength(methodSize); + ReturnKind returnKind = RT_Illegal; + + switch (compiler->info.compRetType) + { + case TYP_REF: + returnKind = RT_Object; + break; + case TYP_BYREF: + returnKind = RT_ByRef; + break; + case TYP_STRUCT: + { + CORINFO_CLASS_HANDLE structType = compiler->info.compMethodInfo->args.retTypeClass; + if (compiler->IsMultiRegReturnedType(structType)) + { + BYTE gcPtrs[2] = { TYPE_GC_NONE, TYPE_GC_NONE }; + compiler->info.compCompHnd->getClassGClayout(structType, gcPtrs); + + ReturnKind first = GCTypeToReturnKind((CorInfoGCType)gcPtrs[0]); + ReturnKind second = GCTypeToReturnKind((CorInfoGCType)gcPtrs[1]); + + returnKind = GetStructReturnKind(first, second); + } + else + { + returnKind = RT_Scalar; + } + break; + } + default: + returnKind = RT_Scalar; + } + + _ASSERTE(returnKind != RT_Illegal); + gcInfoEncoderWithLog->SetReturnKind(returnKind); + if (compiler->isFramePointerUsed()) { gcInfoEncoderWithLog->SetStackBaseRegister(REG_FPBASE); } - if (compiler->info.compIsVarArgs) { diff --git a/src/vm/amd64/AsmHelpers.asm b/src/vm/amd64/AsmHelpers.asm index 1522670146..4563a060b3 100644 --- a/src/vm/amd64/AsmHelpers.asm +++ b/src/vm/amd64/AsmHelpers.asm @@ -23,9 +23,7 @@ extern ThePreStub:proc extern ProfileEnter:proc extern ProfileLeave:proc extern ProfileTailcall:proc -extern OnHijackObjectWorker:proc -extern OnHijackInteriorPointerWorker:proc -extern OnHijackScalarWorker:proc +extern OnHijackWorker:proc extern JIT_RareDisableHelperWorker:proc ifdef _DEBUG @@ -431,54 +429,8 @@ endif ; _DEBUG ; A JITted method's return address was hijacked to return to us here. -; -;VOID __stdcall OnHijackObjectTripThread(); -NESTED_ENTRY OnHijackObjectTripThread, _TEXT - - ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay - ; and HijackObjectArgs - push rax ; make room for the real return address (Rip) - PUSH_CALLEE_SAVED_REGISTERS - push_vol_reg rax - mov rcx, rsp - - alloc_stack 20h - - END_PROLOGUE - - call OnHijackObjectWorker - - add rsp, 20h - pop rax - POP_CALLEE_SAVED_REGISTERS - ret ; return to the correct place, adjusted by our caller -NESTED_END OnHijackObjectTripThread, _TEXT - - -; VOID OnHijackInteriorPointerTripThread() -NESTED_ENTRY OnHijackInteriorPointerTripThread, _TEXT - - ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay - ; and HijackObjectArgs - push rax ; make room for the real return address (Rip) - PUSH_CALLEE_SAVED_REGISTERS - push_vol_reg rax - mov rcx, rsp - - alloc_stack 20h - - END_PROLOGUE - - call OnHijackInteriorPointerWorker - - add rsp, 20h - pop rax - POP_CALLEE_SAVED_REGISTERS - ret ; return to the correct place, adjusted by our caller -NESTED_END OnHijackInteriorPointerTripThread, _TEXT - -; VOID OnHijackScalarTripThread() -NESTED_ENTRY OnHijackScalarTripThread, _TEXT +; VOID OnHijackTripThread() +NESTED_ENTRY OnHijackTripThread, _TEXT ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay ; and HijackObjectArgs @@ -493,7 +445,7 @@ NESTED_ENTRY OnHijackScalarTripThread, _TEXT END_PROLOGUE - call OnHijackScalarWorker + call OnHijackWorker movdqa xmm0, [rsp + 20h] @@ -501,7 +453,7 @@ NESTED_ENTRY OnHijackScalarTripThread, _TEXT pop rax POP_CALLEE_SAVED_REGISTERS ret ; return to the correct place, adjusted by our caller -NESTED_END OnHijackScalarTripThread, _TEXT +NESTED_END OnHijackTripThread, _TEXT ; diff --git a/src/vm/amd64/cgenamd64.cpp b/src/vm/amd64/cgenamd64.cpp index d5698005be..1f7d20a4fb 100644 --- a/src/vm/amd64/cgenamd64.cpp +++ b/src/vm/amd64/cgenamd64.cpp @@ -304,8 +304,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) RETURN; } -// The HijackFrame has to know the registers that are pushed by OnHijackObjectTripThread -// and OnHijackScalarTripThread, so all three are implemented together. +// The HijackFrame has to know the registers that are pushed by OnHijackTripThread void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) { CONTRACTL { diff --git a/src/vm/amd64/cgencpu.h b/src/vm/amd64/cgencpu.h index 409b8e5159..258ac38915 100644 --- a/src/vm/amd64/cgencpu.h +++ b/src/vm/amd64/cgencpu.h @@ -479,13 +479,13 @@ struct DECLSPEC_ALIGN(8) UMEntryThunkCode struct HijackArgs { -#ifndef PLATFORM_UNIX +#ifndef FEATURE_MULTIREG_RETURN union { ULONG64 Rax; - ULONG64 ReturnValue; + ULONG64 ReturnValue[1]; }; -#else // PLATFORM_UNIX +#else // FEATURE_MULTIREG_RETURN union { struct diff --git a/src/vm/amd64/unixasmhelpers.S b/src/vm/amd64/unixasmhelpers.S index 7db8e526f1..837dcf9044 100644 --- a/src/vm/amd64/unixasmhelpers.S +++ b/src/vm/amd64/unixasmhelpers.S @@ -176,7 +176,7 @@ NESTED_END JIT_RareDisableHelper, _TEXT //------------------------------------------------ // OnHijackScalarTripThread // -NESTED_ENTRY OnHijackScalarTripThread, _TEXT, NoHandler +NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler // Make room for the real return address (rip) push_register rax @@ -198,7 +198,7 @@ NESTED_ENTRY OnHijackScalarTripThread, _TEXT, NoHandler END_PROLOGUE - call C_FUNC(OnHijackScalarWorker) + call C_FUNC(OnHijackWorker) movdqa xmm0, [rsp] movdqa xmm1, [rsp+0x10] @@ -209,111 +209,7 @@ NESTED_ENTRY OnHijackScalarTripThread, _TEXT, NoHandler POP_CALLEE_SAVED_REGISTERS ret -NESTED_END OnHijackScalarTripThread, _TEXT - -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING -//------------------------------------------------ -// OnHijackStructInRegsTripThread -// -NESTED_ENTRY OnHijackStructInRegsTripThread, _TEXT, NoHandler - - // Make room for the real return address (rip) - push_register rax - - PUSH_CALLEE_SAVED_REGISTERS - - push_register rdx - // Push rax again - this is where part of the struct gets returned - push_register rax - - mov rdi, rsp - - alloc_stack 0x28 - - // First float return register - movdqa [rsp], xmm0 - // Second float return register - movdqa [rsp+0x10], xmm1 - - END_PROLOGUE - - call C_FUNC(OnHijackStructInRegsWorker) - - movdqa xmm0, [rsp] - movdqa xmm1, [rsp+0x10] - free_stack 0x28 - pop_register rax - pop_register rdx - - POP_CALLEE_SAVED_REGISTERS - ret - -NESTED_END OnHijackStructInRegsTripThread, _TEXT -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING - -//------------------------------------------------ -// OnHijackObjectTripThread -// -NESTED_ENTRY OnHijackObjectTripThread, _TEXT, NoHandler - - // Make room for the real return address (rip) - push_register rax - - PUSH_CALLEE_SAVED_REGISTERS - - push_register rdx - // Push rax again - this is where integer/pointer return values are returned - push_register rax - - mov rdi, rsp - - // align stack - alloc_stack 0x8 - - END_PROLOGUE - - call C_FUNC(OnHijackObjectWorker) - - free_stack 0x8 - pop_register rax - pop_register rdx - - POP_CALLEE_SAVED_REGISTERS - ret - -NESTED_END OnHijackObjectTripThread, _TEXT - -//------------------------------------------------ -// OnHijackInteriorPointerTripThread -// -NESTED_ENTRY OnHijackInteriorPointerTripThread, _TEXT, NoHandler - - // Make room for the real return address (rip) - push_register rax - - PUSH_CALLEE_SAVED_REGISTERS - - push_register rdx - // Push rax again - this is where integer/pointer return values are returned - push_register rax - - mov rdi, rsp - - // align stack - alloc_stack 0x8 - - END_PROLOGUE - - call C_FUNC(OnHijackInteriorPointerWorker) - - free_stack 0x8 - pop_register rax - pop_register rdx - - POP_CALLEE_SAVED_REGISTERS - ret - -NESTED_END OnHijackInteriorPointerTripThread, _TEXT +NESTED_END OnHijackTripThread, _TEXT #endif // FEATURE_HIJACK diff --git a/src/vm/arm/asmhelpers.S b/src/vm/arm/asmhelpers.S index d269186ab4..55bdd9fcba 100644 --- a/src/vm/arm/asmhelpers.S +++ b/src/vm/arm/asmhelpers.S @@ -1364,34 +1364,8 @@ DelayLoad_Helper\suffix: #ifdef FEATURE_HIJACK // ------------------------------------------------------------------ -// Hijack function for functions which return a reference type - NESTED_ENTRY OnHijackObjectTripThread, _TEXT, NoHandler - PROLOG_PUSH "{r0,r4-r11,lr}" - - CHECK_STACK_ALIGNMENT - - mov r0, sp - bl OnHijackObjectWorker - - EPILOG_POP "{r0,r4-r11,pc}" - NESTED_END OnHijackObjectTripThread, _TEXT - -// ------------------------------------------------------------------ -// Hijack function for functions which return an interior pointer within an object allocated in managed heap - NESTED_ENTRY OnHijackInteriorPointerTripThread, _TEXT, NoHandler - PROLOG_PUSH "{r0,r4-r11,lr}" - - CHECK_STACK_ALIGNMENT - - mov r0, sp - bl C_FUNC(OnHijackInteriorPointerWorker) - - EPILOG_POP "{r0,r4-r11,pc}" - NESTED_END OnHijackInteriorPointerTripThread, _TEXT - -// ------------------------------------------------------------------ // Hijack function for functions which return a value type - NESTED_ENTRY OnHijackScalarTripThread, _TEXT, NoHandler + NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler PROLOG_PUSH "{r0,r4-r11,lr}" PROLOG_VPUSH "{d0-d3}" // saving as d0-d3 can have the floating point return value @@ -1401,13 +1375,13 @@ DelayLoad_Helper\suffix: CHECK_STACK_ALIGNMENT add r0, sp, #40 - bl C_FUNC(OnHijackScalarWorker) + bl C_FUNC(OnHijackWorker) free_stack 4 EPILOG_POP "{r1}" EPILOG_VPOP "{d0-d3}" EPILOG_POP "{r0,r4-r11,pc}" - NESTED_END OnHijackScalarTripThread, _TEXT + NESTED_END OnHijackTripThread, _TEXT #endif diff --git a/src/vm/arm/asmhelpers.asm b/src/vm/arm/asmhelpers.asm index 283d8379b3..3a6d446e60 100644 --- a/src/vm/arm/asmhelpers.asm +++ b/src/vm/arm/asmhelpers.asm @@ -59,9 +59,7 @@ #endif #ifdef FEATURE_HIJACK - IMPORT OnHijackObjectWorker - IMPORT OnHijackInteriorPointerWorker - IMPORT OnHijackScalarWorker + IMPORT OnHijackWorker #endif ;FEATURE_HIJACK IMPORT GetCurrentSavedRedirectContext @@ -1694,26 +1692,13 @@ Done ; ------------------------------------------------------------------ ; Hijack function for functions which return a reference type - NESTED_ENTRY OnHijackObjectTripThread + NESTED_ENTRY OnHijackGCTripThread PROLOG_PUSH {r0,r4-r11,lr} CHECK_STACK_ALIGNMENT mov r0, sp - bl OnHijackObjectWorker - - EPILOG_POP {r0,r4-r11,pc} - NESTED_END - -; ------------------------------------------------------------------ -; Hijack function for functions which return an interior pointer within an object allocated in managed heap - NESTED_ENTRY OnHijackInteriorPointerTripThread - PROLOG_PUSH {r0,r4-r11,lr} - - CHECK_STACK_ALIGNMENT - - mov r0, sp - bl OnHijackInteriorPointerWorker + bl OnHijackWorker EPILOG_POP {r0,r4-r11,pc} NESTED_END @@ -1730,7 +1715,7 @@ Done CHECK_STACK_ALIGNMENT add r0, sp, #40 - bl OnHijackScalarWorker + bl OnHijackWorker EPILOG_STACK_FREE 4 EPILOG_POP {r1} diff --git a/src/vm/arm/cgencpu.h b/src/vm/arm/cgencpu.h index 30573eb571..936fdabafb 100644 --- a/src/vm/arm/cgencpu.h +++ b/src/vm/arm/cgencpu.h @@ -959,9 +959,9 @@ struct HijackArgs union { DWORD R0; - size_t ReturnValue; // this may not be the return value when return is >32bits or return value is in VFP reg - // but it works for us as this is only used by functions OnHijackObjectWorker() - // and OnHijackInteriorPointerWorker() (where return is an address) + size_t ReturnValue[1]; // this may not be the return value when return is >32bits + // or return value is in VFP reg but it works for us as + // this is only used by functions OnHijackWorker() }; // diff --git a/src/vm/arm64/asmhelpers.asm b/src/vm/arm64/asmhelpers.asm index 89e6710a96..1369bd80d7 100644 --- a/src/vm/arm64/asmhelpers.asm +++ b/src/vm/arm64/asmhelpers.asm @@ -31,9 +31,7 @@ IMPORT GetCurrentSavedRedirectContext IMPORT LinkFrameAndThrow IMPORT FixContextHandler - IMPORT OnHijackObjectWorker - IMPORT OnHijackInteriorPointerWorker - IMPORT OnHijackScalarWorker + IMPORT OnHijackWorker #ifdef FEATURE_READYTORUN IMPORT DynamicHelperWorker #endif @@ -872,58 +870,8 @@ UM2MThunk_WrapperHelper_RegArgumentsSetup #ifdef FEATURE_HIJACK ; ------------------------------------------------------------------ -; Hijack function for functions which return a reference type - NESTED_ENTRY OnHijackObjectTripThread - PROLOG_SAVE_REG_PAIR fp, lr, #-112! - ; Spill callee saved registers - PROLOG_SAVE_REG_PAIR x19, x20, #16 - PROLOG_SAVE_REG_PAIR x21, x22, #32 - PROLOG_SAVE_REG_PAIR x23, x24, #48 - PROLOG_SAVE_REG_PAIR x25, x26, #64 - PROLOG_SAVE_REG_PAIR x27, x28, #80 - - str x0, [sp, #96] - mov x0, sp - bl OnHijackObjectWorker - ldr x0, [sp, #96] - - EPILOG_RESTORE_REG_PAIR x19, x20, #16 - EPILOG_RESTORE_REG_PAIR x21, x22, #32 - EPILOG_RESTORE_REG_PAIR x23, x24, #48 - EPILOG_RESTORE_REG_PAIR x25, x26, #64 - EPILOG_RESTORE_REG_PAIR x27, x28, #80 - EPILOG_RESTORE_REG_PAIR fp, lr, #112! - EPILOG_RETURN - NESTED_END - -; ------------------------------------------------------------------ -; Hijack function for functions which return an interior pointer within an object allocated in managed heap - NESTED_ENTRY OnHijackInteriorPointerTripThread - PROLOG_SAVE_REG_PAIR fp, lr, #-112! - ; Spill callee saved registers - PROLOG_SAVE_REG_PAIR x19, x20, #16 - PROLOG_SAVE_REG_PAIR x21, x22, #32 - PROLOG_SAVE_REG_PAIR x23, x24, #48 - PROLOG_SAVE_REG_PAIR x25, x26, #64 - PROLOG_SAVE_REG_PAIR x27, x28, #80 - - str x0, [sp, #96] - mov x0, sp - bl OnHijackInteriorPointerWorker - ldr x0, [sp, #96] - - EPILOG_RESTORE_REG_PAIR x19, x20, #16 - EPILOG_RESTORE_REG_PAIR x21, x22, #32 - EPILOG_RESTORE_REG_PAIR x23, x24, #48 - EPILOG_RESTORE_REG_PAIR x25, x26, #64 - EPILOG_RESTORE_REG_PAIR x27, x28, #80 - EPILOG_RESTORE_REG_PAIR fp, lr, #112! - EPILOG_RETURN - NESTED_END - -; ------------------------------------------------------------------ ; Hijack function for functions which return a scalar type or a struct (value type) - NESTED_ENTRY OnHijackScalarTripThread + NESTED_ENTRY OnHijackTripThread PROLOG_SAVE_REG_PAIR fp, lr, #-144! ; Spill callee saved registers PROLOG_SAVE_REG_PAIR x19, x20, #16 @@ -940,7 +888,7 @@ UM2MThunk_WrapperHelper_RegArgumentsSetup stp d2, d3, [sp, #128] mov x0, sp - bl OnHijackScalarWorker + bl OnHijackWorker ; restore any integral return value(s) ldp x0, x1, [sp, #96] diff --git a/src/vm/codeman.cpp b/src/vm/codeman.cpp index 9ce6bb2a20..89084dbe85 100644 --- a/src/vm/codeman.cpp +++ b/src/vm/codeman.cpp @@ -6417,7 +6417,9 @@ UINT32 ReadyToRunJitManager::JitTokenToGCInfoVersion(const METHODTOKEN& MethodTo SUPPORTS_DAC; } CONTRACTL_END; - return GCINFO_VERSION; + READYTORUN_HEADER * header = JitTokenToReadyToRunInfo(MethodToken)->GetImage()->GetReadyToRunHeader(); + + return GCInfoToken::ReadyToRunVersionToGcInfoVersion(header->MajorVersion); } PTR_RUNTIME_FUNCTION ReadyToRunJitManager::JitTokenToRuntimeFunction(const METHODTOKEN& MethodToken) diff --git a/src/vm/crossgencompile.cpp b/src/vm/crossgencompile.cpp index 36ba0f6cbf..85859c2d82 100644 --- a/src/vm/crossgencompile.cpp +++ b/src/vm/crossgencompile.cpp @@ -334,6 +334,11 @@ void GCFrame::GcScanRoots(promote_func *fn, ScanContext* sc) UNREACHABLE(); } +void HijackFrame::GcScanRoots(promote_func *fn, ScanContext* sc) +{ + UNREACHABLE(); +} + VOID GCFrame::Pop() { } diff --git a/src/vm/eetwain.cpp b/src/vm/eetwain.cpp index 82b76f69e0..165335f33a 100644 --- a/src/vm/eetwain.cpp +++ b/src/vm/eetwain.cpp @@ -1507,8 +1507,8 @@ unsigned EECodeManager::FindEndOfLastInterruptibleRegion(unsigned curOffset, #ifndef DACCESS_COMPILE GcInfoDecoder gcInfoDecoder( gcInfoToken, - DECODE_FOR_RANGES_CALLBACK, - 0); + DECODE_FOR_RANGES_CALLBACK + ); FindEndOfLastInterruptibleRegionState state; state.curOffset = curOffset; @@ -4838,9 +4838,8 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY pRD, #ifdef _DEBUG GcInfoDecoder _gcInfoDecoder( gcInfoToken, - DECODE_CODE_LENGTH, - 0 - ); + DECODE_CODE_LENGTH + ); // We only use override offset for wantsReportOnlyLeaf _ASSERTE(_gcInfoDecoder.WantsReportOnlyLeaf()); @@ -5051,8 +5050,7 @@ OBJECTREF* EECodeManager::GetAddrOfSecurityObject(CrawlFrame *pCF) GcInfoDecoder gcInfoDecoder( gcInfoToken, - DECODE_SECURITY_OBJECT, - 0 + DECODE_SECURITY_OBJECT ); INT32 spOffset = gcInfoDecoder.GetSecurityObjectStackSlot(); @@ -5270,8 +5268,7 @@ GenericParamContextType EECodeManager::GetParamContextType(PREGDISPLAY pCont GcInfoDecoder gcInfoDecoder( gcInfoToken, - GcInfoDecoderFlags (DECODE_GENERICS_INST_CONTEXT), - 0 + GcInfoDecoderFlags (DECODE_GENERICS_INST_CONTEXT) ); INT32 spOffsetGenericsContext = gcInfoDecoder.GetGenericsInstContextStackSlot(); @@ -5362,8 +5359,7 @@ PTR_VOID EECodeManager::GetExactGenericsToken(SIZE_T baseStackSlot, GcInfoDecoder gcInfoDecoder( gcInfoToken, - GcInfoDecoderFlags (DECODE_PSP_SYM | DECODE_GENERICS_INST_CONTEXT), - 0 + GcInfoDecoderFlags (DECODE_PSP_SYM | DECODE_GENERICS_INST_CONTEXT) ); INT32 spOffsetGenericsContext = gcInfoDecoder.GetGenericsInstContextStackSlot(); @@ -5467,8 +5463,7 @@ void * EECodeManager::GetGSCookieAddr(PREGDISPLAY pContext, GcInfoDecoder gcInfoDecoder( gcInfoToken, - DECODE_GS_COOKIE, - 0 + DECODE_GS_COOKIE ); INT32 spOffsetGSCookie = gcInfoDecoder.GetGSCookieStackSlot(); @@ -5578,8 +5573,7 @@ size_t EECodeManager::GetFunctionSize(GCInfoToken gcInfoToken) GcInfoDecoder gcInfoDecoder( gcInfoToken, - DECODE_CODE_LENGTH, - 0 + DECODE_CODE_LENGTH ); UINT32 codeLength = gcInfoDecoder.GetCodeLength(); diff --git a/src/vm/frames.cpp b/src/vm/frames.cpp index eff32f333a..b99c4510bc 100644 --- a/src/vm/frames.cpp +++ b/src/vm/frames.cpp @@ -1132,6 +1132,60 @@ BOOL IsProtectedByGCFrame(OBJECTREF *ppObjectRef) #endif //!DACCESS_COMPILE +#ifdef FEATURE_HIJACK + +void HijackFrame::GcScanRoots(promote_func *fn, ScanContext* sc) +{ + LIMITED_METHOD_CONTRACT; + + ReturnKind returnKind = m_Thread->GetHijackReturnKind(); + _ASSERTE(IsValidReturnKind(returnKind)); + + int regNo = 0; + bool moreRegisters; + + do + { + moreRegisters = false; + PTR_PTR_Object objPtr = dac_cast<PTR_PTR_Object>(&m_Args->ReturnValue[regNo]); + + switch (returnKind) + { +#ifdef _TARGET_X86_ + case RT_Float: // Fall through +#endif + case RT_Scalar: + // nothing to report + break; + + case RT_Object: + LOG((LF_GC, INFO3, "Hijack Frame Promoting Object" FMT_ADDR "to", + DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr)))); + (*fn)(objPtr, sc, CHECK_APP_DOMAIN); + LOG((LF_GC, INFO3, FMT_ADDR "\n", DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr)))); + break; + + case RT_ByRef: + LOG((LF_GC, INFO3, "Hijack Frame Carefully Promoting pointer" FMT_ADDR "to", + DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr)))); + PromoteCarefully(fn, objPtr, sc, GC_CALL_INTERIOR | GC_CALL_CHECK_APP_DOMAIN); + LOG((LF_GC, INFO3, FMT_ADDR "\n", DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr)))); + break; + + default: +#ifdef FEATURE_MULTIREG_RETURN + moreRegisters = true; + regNo++; + returnKind = ExtractRegReturnKind(returnKind, regNo); +#else + _ASSERTE(!"Impossible two bit encoding"); +#endif + } + } while (moreRegisters); +} + +#endif // FEATURE_HIJACK + void ProtectByRefsFrame::GcScanRoots(promote_func *fn, ScanContext *sc) { CONTRACTL diff --git a/src/vm/frames.h b/src/vm/frames.h index 7b55c36884..0926f29cea 100644 --- a/src/vm/frames.h +++ b/src/vm/frames.h @@ -2171,10 +2171,11 @@ public: } virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void GcScanRoots(promote_func *fn, ScanContext* sc); - // HijackFrames are created by trip functions. See OnHijackObjectTripThread() - // and OnHijackScalarTripThread(). They are real C++ objects on the stack. So - // it's a public function -- but that doesn't mean you should make some. + // HijackFrames are created by trip functions. See OnHijackTripThread() + // They are real C++ objects on the stack. + // So, it's a public function -- but that doesn't mean you should make some. HijackFrame(LPVOID returnAddress, Thread *thread, HijackArgs *args); protected: diff --git a/src/vm/gcenv.ee.cpp b/src/vm/gcenv.ee.cpp index da36e726ba..2f1e4e8200 100644 --- a/src/vm/gcenv.ee.cpp +++ b/src/vm/gcenv.ee.cpp @@ -133,8 +133,7 @@ inline bool SafeToReportGenericParamContext(CrawlFrame* pCF) #else // USE_GC_INFO_DECODER GcInfoDecoder gcInfoDecoder(pCF->GetGCInfoToken(), - DECODE_PROLOG_LENGTH, - 0); + DECODE_PROLOG_LENGTH); UINT32 prologLength = gcInfoDecoder.GetPrologSize(); if (pCF->GetRelOffset() < prologLength) { @@ -200,7 +199,7 @@ bool FindFirstInterruptiblePointStateCB( unsigned FindFirstInterruptiblePoint(CrawlFrame* pCF, unsigned offs, unsigned endOffs) { GCInfoToken gcInfoToken = pCF->GetGCInfoToken(); - GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_FOR_RANGES_CALLBACK, 0); + GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_FOR_RANGES_CALLBACK); FindFirstInterruptiblePointState state; state.offs = offs; @@ -284,8 +283,7 @@ StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData) GCInfoToken gcInfoToken = pCF->GetGCInfoToken(); GcInfoDecoder _gcInfoDecoder( gcInfoToken, - DECODE_CODE_LENGTH, - 0 + DECODE_CODE_LENGTH ); if(_gcInfoDecoder.WantsReportOnlyLeaf()) diff --git a/src/vm/gcinfodecoder.cpp b/src/vm/gcinfodecoder.cpp index 351e221d82..ef237a2768 100644 --- a/src/vm/gcinfodecoder.cpp +++ b/src/vm/gcinfodecoder.cpp @@ -81,7 +81,6 @@ bool GcInfoDecoder::SetIsInterruptibleCB (UINT32 startOffset, UINT32 stopOffset, return fStop; } - GcInfoDecoder::GcInfoDecoder( GCInfoToken gcInfoToken, GcInfoDecoderFlags flags, @@ -90,11 +89,12 @@ GcInfoDecoder::GcInfoDecoder( : m_Reader(dac_cast<PTR_CBYTE>(gcInfoToken.Info)) , m_InstructionOffset(breakOffset) , m_IsInterruptible(false) + , m_ReturnKind(RT_Illegal) #ifdef _DEBUG , m_Flags( flags ) , m_GcInfoAddress(dac_cast<PTR_CBYTE>(gcInfoToken.Info)) - , m_Version(gcInfoToken.Version) #endif + , m_Version(gcInfoToken.Version) { _ASSERTE( (flags & (DECODE_INTERRUPTIBILITY | DECODE_GC_LIFETIMES)) || (0 == breakOffset) ); @@ -116,7 +116,8 @@ GcInfoDecoder::GcInfoDecoder( } else { - headerFlags = (GcInfoHeaderFlags) m_Reader.Read(GC_INFO_FLAGS_BIT_SIZE); + int numFlagBits = (m_Version == 1) ? GC_INFO_FLAGS_BIT_SIZE_VERSION_1 : GC_INFO_FLAGS_BIT_SIZE; + headerFlags = (GcInfoHeaderFlags) m_Reader.Read(numFlagBits); } m_IsVarArg = headerFlags & GC_INFO_IS_VARARG; @@ -129,12 +130,35 @@ GcInfoDecoder::GcInfoDecoder( int hasStackBaseRegister = headerFlags & GC_INFO_HAS_STACK_BASE_REGISTER; m_WantsReportOnlyLeaf = ((headerFlags & GC_INFO_WANTS_REPORT_ONLY_LEAF) != 0); int hasSizeOfEditAndContinuePreservedArea = headerFlags & GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS; + + int hasReversePInvokeFrame = false; + if (gcInfoToken.IsReversePInvokeFrameAvailable()) + { + hasReversePInvokeFrame = headerFlags & GC_INFO_REVERSE_PINVOKE_FRAME; + } + + if (gcInfoToken.IsReturnKindAvailable()) + { + int returnKindBits = (slimHeader) ? SIZE_OF_RETURN_KIND_IN_SLIM_HEADER : SIZE_OF_RETURN_KIND_IN_FAT_HEADER; + m_ReturnKind = + (ReturnKind)((UINT32)m_Reader.Read(returnKindBits)); + } + else + { +#ifndef _TARGET_X86_ + m_ReturnKind = RT_Unset; +#endif // ! _TARGET_X86_ + } + + if (flags == DECODE_RETURN_KIND) { + // Bail, if we've decoded enough, + return; + } m_CodeLength = (UINT32) DENORMALIZE_CODE_LENGTH((UINT32) m_Reader.DecodeVarLengthUnsigned(CODE_LENGTH_ENCBASE)); - if (flags == DECODE_CODE_LENGTH) - { - // If we are only interested in the code length, then bail out now. + if (flags == DECODE_CODE_LENGTH) { + // Bail, if we've decoded enough, return; } @@ -165,12 +189,11 @@ GcInfoDecoder::GcInfoDecoder( m_ValidRangeStart = m_ValidRangeEnd = 0; } - if (flags == DECODE_PROLOG_LENGTH) - { - // if we are only interested in the prolog size, then bail out now + if (flags == DECODE_PROLOG_LENGTH) { + // Bail, if we've decoded enough, return; } - + // Decode the offset to the security object. if(hasSecurityObject) { @@ -180,9 +203,9 @@ GcInfoDecoder::GcInfoDecoder( { m_SecurityObjectStackSlot = NO_SECURITY_OBJECT; } - if (flags == DECODE_SECURITY_OBJECT) - { - // If we are only interested in the security object, then bail out now. + + if (flags == DECODE_SECURITY_OBJECT) { + // Bail, if we've decoded enough, return; } @@ -195,9 +218,9 @@ GcInfoDecoder::GcInfoDecoder( { m_GSCookieStackSlot = NO_GS_COOKIE; } - if (flags == DECODE_GS_COOKIE) - { - // If we are only interested in the GS cookie, then bail out now. + + if (flags == DECODE_GS_COOKIE) { + // Bail, if we've decoded enough, return; } @@ -211,9 +234,9 @@ GcInfoDecoder::GcInfoDecoder( { m_PSPSymStackSlot = NO_PSP_SYM; } - if (flags == DECODE_PSP_SYM) - { - // If we are only interested in the PSPSym, then bail out now. + + if (flags == DECODE_PSP_SYM) { + // Bail, if we've decoded enough, return; } @@ -226,9 +249,9 @@ GcInfoDecoder::GcInfoDecoder( { m_GenericsInstContextStackSlot = NO_GENERICS_INST_CONTEXT; } - if (flags == DECODE_GENERICS_INST_CONTEXT) - { - // If we are only interested in the generics token, then bail out now. + + if (flags == DECODE_GENERICS_INST_CONTEXT) { + // Bail, if we've decoded enough, return; } @@ -257,6 +280,16 @@ GcInfoDecoder::GcInfoDecoder( m_SizeOfEditAndContinuePreservedArea = NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA; } + if (hasReversePInvokeFrame) + { + m_ReversePInvokeFrameSlot = (INT32)m_Reader.DecodeVarLengthSigned(REVERSE_PINVOKE_FRAME_ENCBASE); + } + else + { + m_ReversePInvokeFrameSlot = NO_REVERSE_PINVOKE_FRAME; + } + + #ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA if (slimHeader) { @@ -334,7 +367,7 @@ bool GcInfoDecoder::HasMethodTableGenericsInstContext() // a call-return offset with partially-interruptible GC info? bool GcInfoDecoder::IsSafePoint(UINT32 codeOffset) { - _ASSERTE(m_Flags == 0 && m_InstructionOffset == 0); + _ASSERTE(m_Flags == DECODE_EVERYTHING && m_InstructionOffset == 0); if(m_NumSafePoints == 0) return false; @@ -455,6 +488,12 @@ INT32 GcInfoDecoder::GetGSCookieStackSlot() return m_GSCookieStackSlot; } +INT32 GcInfoDecoder::GetReversePInvokeStackSlot() +{ + _ASSERTE(m_Flags & DECODE_REVERSE_PINVOKE_VAR); + return m_ReversePInvokeStackSlot; +} + UINT32 GcInfoDecoder::GetGSCookieValidRangeStart() { _ASSERTE( m_Flags & DECODE_GS_COOKIE ); @@ -503,6 +542,13 @@ UINT32 GcInfoDecoder::GetCodeLength() return m_CodeLength; } +ReturnKind GcInfoDecoder::GetReturnKind() +{ + // SUPPORTS_DAC; + _ASSERTE( m_Flags & DECODE_RETURN_KIND ); + return m_ReturnKind; +} + UINT32 GcInfoDecoder::GetStackBaseRegister() { return m_StackBaseRegister; diff --git a/src/vm/i386/asmhelpers.asm b/src/vm/i386/asmhelpers.asm index dc3956cc54..66a22b7962 100644 --- a/src/vm/i386/asmhelpers.asm +++ b/src/vm/i386/asmhelpers.asm @@ -41,9 +41,7 @@ EXTERN _StubRareDisableTHROWWorker@4:PROC EXTERN __imp__TlsGetValue@4:DWORD TlsGetValue PROTO stdcall ifdef FEATURE_HIJACK -EXTERN _OnHijackObjectWorker@4:PROC -EXTERN _OnHijackInteriorPointerWorker@4:PROC -EXTERN _OnHijackScalarWorker@4:PROC +EXTERN _OnHijackWorker@4:PROC endif ;FEATURE_HIJACK EXTERN _COMPlusEndCatch@20:PROC EXTERN _COMPlusFrameHandler:PROC @@ -932,79 +930,9 @@ endif ifdef FEATURE_HIJACK -; A JITted method's return address was hijacked to return to us here. What we do -; is make a __cdecl call with 2 ints. One is the return value we wish to preserve. -; The other is space for our real return address. -; -;VOID __stdcall OnHijackObjectTripThread(); -OnHijackObjectTripThread PROC stdcall public - - ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay - ; and HijackArgs - push eax ; make room for the real return address (Eip) - push ebp - push eax - push ecx - push edx - push ebx - push esi - push edi - - ; unused space for floating point state - sub esp,12 - - push esp - call _OnHijackObjectWorker@4 - - ; unused space for floating point state - add esp,12 - - pop edi - pop esi - pop ebx - pop edx - pop ecx - pop eax - pop ebp - retn ; return to the correct place, adjusted by our caller -OnHijackObjectTripThread ENDP - - -; VOID OnHijackInteriorPointerTripThread() -OnHijackInteriorPointerTripThread PROC stdcall public - - ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay - ; and HijackArgs - push eax ; make room for the real return address (Eip) - push ebp - push eax - push ecx - push edx - push ebx - push esi - push edi - - ; unused space for floating point state - sub esp,12 - - push esp - call _OnHijackInteriorPointerWorker@4 - - ; unused space for floating point state - add esp,12 - - pop edi - pop esi - pop ebx - pop edx - pop ecx - pop eax - pop ebp - retn ; return to the correct place, adjusted by our caller -OnHijackInteriorPointerTripThread ENDP - -; VOID OnHijackScalarTripThread() -OnHijackScalarTripThread PROC stdcall public +; A JITted method's return address was hijacked to return to us here. +; VOID OnHijackTripThread() +OnHijackTripThread PROC stdcall public ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay ; and HijackArgs @@ -1021,7 +949,7 @@ OnHijackScalarTripThread PROC stdcall public sub esp,12 push esp - call _OnHijackScalarWorker@4 + call _OnHijackWorker@4 ; unused space for floating point state add esp,12 @@ -1034,10 +962,10 @@ OnHijackScalarTripThread PROC stdcall public pop eax pop ebp retn ; return to the correct place, adjusted by our caller -OnHijackScalarTripThread ENDP +OnHijackTripThread ENDP -; VOID OnHijackFloatingPointTripThread() -OnHijackFloatingPointTripThread PROC stdcall public +; VOID OnHijackFPTripThread() +OnHijackFPTripThread PROC stdcall public ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay ; and HijackArgs @@ -1057,7 +985,7 @@ OnHijackFloatingPointTripThread PROC stdcall public fstp tbyte ptr [esp] push esp - call _OnHijackScalarWorker@4 + call _OnHijackWorker@4 ; restore top of the floating point stack fld tbyte ptr [esp] @@ -1072,7 +1000,7 @@ OnHijackFloatingPointTripThread PROC stdcall public pop eax pop ebp retn ; return to the correct place, adjusted by our caller -OnHijackFloatingPointTripThread ENDP +OnHijackFPTripThread ENDP endif ; FEATURE_HIJACK diff --git a/src/vm/i386/cgencpu.h b/src/vm/i386/cgencpu.h index d1684290e4..2da98821bc 100644 --- a/src/vm/i386/cgencpu.h +++ b/src/vm/i386/cgencpu.h @@ -513,7 +513,7 @@ struct HijackArgs union { DWORD Eax; - size_t ReturnValue; + size_t ReturnValue[1]; }; DWORD Ebp; union diff --git a/src/vm/i386/cgenx86.cpp b/src/vm/i386/cgenx86.cpp index 48428a1c61..ff2f2df5a3 100644 --- a/src/vm/i386/cgenx86.cpp +++ b/src/vm/i386/cgenx86.cpp @@ -643,8 +643,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) RETURN; } -// The HijackFrame has to know the registers that are pushed by OnHijackObjectTripThread -// and OnHijackScalarTripThread, so all three are implemented together. +// The HijackFrame has to know the registers that are pushed by OnHijackTripThread void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) { CONTRACTL { diff --git a/src/vm/threads.cpp b/src/vm/threads.cpp index d0f19006ec..e7f4ccbf36 100644 --- a/src/vm/threads.cpp +++ b/src/vm/threads.cpp @@ -2238,12 +2238,9 @@ Thread::Thread() #endif m_pAllLoggedTypes = NULL; -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING - m_pHijackReturnTypeClass = NULL; -#endif + m_HijackReturnKind = RT_Illegal; } - //-------------------------------------------------------------------- // Failable initialization occurs here. //-------------------------------------------------------------------- diff --git a/src/vm/threads.h b/src/vm/threads.h index e704c2b4f0..bd81c63432 100644 --- a/src/vm/threads.h +++ b/src/vm/threads.h @@ -143,6 +143,7 @@ #include "mscoree.h" #include "appdomainstack.h" #include "gc.h" +#include "gcinfotypes.h" #include <clrhost.h> class Thread; @@ -630,8 +631,9 @@ enum ThreadpoolThreadType // // Public functions for taking control of a thread at a safe point // -// VOID OnHijackObjectTripThread() - we've hijacked a JIT object-ref return -// VOID OnHijackScalarTripThread() - we've hijacked a JIT non-object ref return +// VOID OnHijackTripThread() - we've hijacked a JIT method +// VOID OnHijackFPTripThread() - we've hijacked a JIT method, +// and need to save the x87 FP stack. // //*************************************************************************** @@ -686,15 +688,9 @@ void InitThreadManager(); #ifdef FEATURE_HIJACK -EXTERN_C void __stdcall OnHijackObjectTripThread(); // hijacked JIT code is returning an objectref -EXTERN_C void __stdcall OnHijackInteriorPointerTripThread(); // hijacked JIT code is returning a byref -EXTERN_C void __stdcall OnHijackScalarTripThread(); // hijacked JIT code is returning a non-objectref, non-FP -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING -EXTERN_C void __stdcall OnHijackStructInRegsTripThread(); // hijacked JIT code is returning a struct in registers -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING - +EXTERN_C void __stdcall OnHijackTripThread(); #ifdef _TARGET_X86_ -EXTERN_C void __stdcall OnHijackFloatingPointTripThread(); // hijacked JIT code is returning an FP value +EXTERN_C void __stdcall OnHijackFPTripThread(); // hijacked JIT code is returning an FP value #endif // _TARGET_X86_ #endif // FEATURE_HIJACK @@ -1016,13 +1012,17 @@ typedef DWORD (*AppropriateWaitFunc) (void *args, DWORD timeout, DWORD option); // unstarted System.Thread), then this instance can be found in the TLS // of that physical thread. -#ifdef FEATURE_HIJACK -EXTERN_C void STDCALL OnHijackObjectWorker(HijackArgs * pArgs); -EXTERN_C void STDCALL OnHijackInteriorPointerWorker(HijackArgs * pArgs); -EXTERN_C void STDCALL OnHijackScalarWorker(HijackArgs * pArgs); -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING -EXTERN_C void STDCALL OnHijackStructInRegsWorker(HijackArgs * pArgs); -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING +// FEATURE_MULTIREG_RETURN is set for platforms where a struct return value +// [GcInfo v2 only] can be returned in multiple registers +// ex: Windows/Unix ARM/ARM64, Unix-AMD64. +// +// +// FEATURE_UNIX_AMD64_STRUCT_PASSING is a specific kind of FEATURE_MULTIREG_RETURN +// [GcInfo v1 and v2] specified by SystemV ABI for AMD64 +// + +#ifdef FEATURE_HIJACK // Hijack function returning +EXTERN_C void STDCALL OnHijackWorker(HijackArgs * pArgs); #endif // FEATURE_HIJACK // This is the code we pass around for Thread.Interrupt, mainly for assertions @@ -1070,12 +1070,7 @@ class Thread: public IUnknown #ifdef FEATURE_HIJACK // MapWin32FaultToCOMPlusException needs access to Thread::IsAddrOfRedirectFunc() friend DWORD MapWin32FaultToCOMPlusException(EXCEPTION_RECORD *pExceptionRecord); - friend void STDCALL OnHijackObjectWorker(HijackArgs *pArgs); - friend void STDCALL OnHijackInteriorPointerWorker(HijackArgs *pArgs); - friend void STDCALL OnHijackScalarWorker(HijackArgs *pArgs); -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING - friend void STDCALL OnHijackStructInRegsWorker(HijackArgs *pArgs); -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING + friend void STDCALL OnHijackWorker(HijackArgs * pArgs); #ifdef PLATFORM_UNIX friend void PALAPI HandleGCSuspensionForInterruptedThread(CONTEXT *interruptedContext); #endif // PLATFORM_UNIX @@ -5577,24 +5572,33 @@ public: _ASSERTE(pAllLoggedTypes != NULL ? m_pAllLoggedTypes == NULL : TRUE); m_pAllLoggedTypes = pAllLoggedTypes; } -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + +#ifdef FEATURE_HIJACK private: - EEClass* m_pHijackReturnTypeClass; + + // By the time a frame is scanned by the runtime, m_pHijackReturnKind always + // identifies the gc-ness of the return register(s) + // If the ReturnKind information is not available from the GcInfo, the runtime + // computes it using the return types's class handle. + + ReturnKind m_HijackReturnKind; + public: - EEClass* GetHijackReturnTypeClass() + + ReturnKind GetHijackReturnKind() { LIMITED_METHOD_CONTRACT; - return m_pHijackReturnTypeClass; + return m_HijackReturnKind; } - void SetHijackReturnTypeClass(EEClass* pClass) + void SetHijackReturnKind(ReturnKind returnKind) { LIMITED_METHOD_CONTRACT; - m_pHijackReturnTypeClass = pClass; + m_HijackReturnKind = returnKind; } -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING +#endif // FEATURE_HIJACK }; // End of class Thread diff --git a/src/vm/threadsuspend.cpp b/src/vm/threadsuspend.cpp index 6d25012bbd..2cf32dfb6b 100644 --- a/src/vm/threadsuspend.cpp +++ b/src/vm/threadsuspend.cpp @@ -6948,8 +6948,7 @@ void Thread::UnhijackThread() // Can't make the following assertion, because sometimes we unhijack after // the hijack has tripped (i.e. in the case we actually got some value from // it. -// _ASSERTE(*m_ppvHJRetAddrPtr == OnHijackObjectTripThread || -// *m_ppvHJRetAddrPtr == OnHijackScalarTripThread); +// _ASSERTE(*m_ppvHJRetAddrPtr == OnHijackTripThread); STRESS_LOG2(LF_SYNC, LL_INFO100, "Unhijacking return address 0x%p for thread %p\n", m_pvHJRetAddr, this); // restore the return address and clear the flag @@ -7147,31 +7146,30 @@ HijackFrame::HijackFrame(LPVOID returnAddress, Thread *thread, HijackArgs *args) m_Thread->SetFrame(this); } -// A hijacked method is returning an ObjectRef to its caller. Note that we bash the -// return address in HijackArgs. -void STDCALL OnHijackObjectWorker(HijackArgs * pArgs) +void STDCALL OnHijackWorker(HijackArgs * pArgs) { - CONTRACTL { + CONTRACTL{ THROWS; GC_TRIGGERS; SO_TOLERANT; } CONTRACTL_END; +#ifdef HIJACK_NONINTERRUPTIBLE_THREADS Thread *thread = GetThread(); #ifdef FEATURE_STACK_PROBE if (GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) == eRudeUnloadAppDomain) { + // Make sure default domain does not see SO. + // probe for our entry point amount and throw if not enough stack RetailStackProbe(ADJUST_PROBE(DEFAULT_ENTRY_PROBE_AMOUNT), thread); } -#endif +#endif // FEATURE_STACK_PROBE CONTRACT_VIOLATION(SOToleranceViolation); -#ifdef HIJACK_NONINTERRUPTIBLE_THREADS - OBJECTREF oref(ObjectToOBJECTREF(*(Object **) &pArgs->ReturnValue)); - FastInterlockAnd((ULONG *) &thread->m_State, ~Thread::TS_Hijacked); + thread->ResetThreadState(Thread::TS_Hijacked); // Fix up our caller's stack, so it can resume from the hijack correctly pArgs->ReturnAddress = (size_t)thread->m_pvHJRetAddr; @@ -7180,238 +7178,148 @@ void STDCALL OnHijackObjectWorker(HijackArgs * pArgs) // we will resume execution. FrameWithCookie<HijackFrame> frame((void *)pArgs->ReturnAddress, thread, pArgs); - GCPROTECT_BEGIN(oref) - { #ifdef _DEBUG - BOOL GCOnTransition = FALSE; - if (g_pConfig->FastGCStressLevel()) { - GCOnTransition = GC_ON_TRANSITIONS (FALSE); - } -#endif + BOOL GCOnTransition = FALSE; + if (g_pConfig->FastGCStressLevel()) { + GCOnTransition = GC_ON_TRANSITIONS(FALSE); + } +#endif // _DEBUG #ifdef TIME_SUSPEND - g_SuspendStatistics.cntHijackTrap++; -#endif + g_SuspendStatistics.cntHijackTrap++; +#endif // TIME_SUSPEND + + CommonTripThread(); - CommonTripThread(); #ifdef _DEBUG - if (g_pConfig->FastGCStressLevel()) { - GC_ON_TRANSITIONS (GCOnTransition); - } -#endif - *((OBJECTREF *) &pArgs->ReturnValue) = oref; + if (g_pConfig->FastGCStressLevel()) { + GC_ON_TRANSITIONS(GCOnTransition); } - GCPROTECT_END(); // trashes oref here! +#endif // _DEBUG frame.Pop(); #else - PORTABILITY_ASSERT("OnHijackObjectWorker not implemented on this platform."); -#endif + PORTABILITY_ASSERT("OnHijackWorker not implemented on this platform."); +#endif // HIJACK_NONINTERRUPTIBLE_THREADS } -// A hijacked method is returning an ObjectRef to its caller. Note that we bash the -// return address in HijackObjectArgs. -void STDCALL OnHijackInteriorPointerWorker(HijackArgs * pArgs) +ReturnKind GetReturnKindFromMethodTable(Thread *pThread, EECodeInfo *codeInfo) { - CONTRACTL { - THROWS; - GC_TRIGGERS; - SO_TOLERANT; - } CONTRACTL_END; - -#ifdef HIJACK_NONINTERRUPTIBLE_THREADS - Thread *thread = GetThread(); - void* ptr = (void*)(pArgs->ReturnValue); - -#ifdef FEATURE_STACK_PROBE - if (GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) == eRudeUnloadAppDomain) - { - RetailStackProbe(ADJUST_PROBE(DEFAULT_ENTRY_PROBE_AMOUNT), thread); - } -#endif - - CONTRACT_VIOLATION(SOToleranceViolation); - - FastInterlockAnd((ULONG *) &thread->m_State, ~Thread::TS_Hijacked); +#ifdef _WIN64 + // For simplicity, we don't hijack in funclets, but if you ever change that, + // be sure to choose the OnHijack... callback type to match that of the FUNCLET + // not the main method (it would probably be Scalar). +#endif // _WIN64 - // Fix up our caller's stack, so it can resume from the hijack correctly - pArgs->ReturnAddress = (size_t)thread->m_pvHJRetAddr; + ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); + // Mark that we are performing a stackwalker like operation on the current thread. + // This is necessary to allow the signature parsing functions to work without triggering any loads + ClrFlsValueSwitch threadStackWalking(TlsIdx_StackWalkerWalkingThread, pThread); - // Build a frame so that stack crawling can proceed from here back to where - // we will resume execution. - FrameWithCookie<HijackFrame> frame((void *)pArgs->ReturnAddress, thread, pArgs); + MethodDesc *methodDesc = codeInfo->GetMethodDesc(); + _ASSERTE(methodDesc != nullptr); - GCPROTECT_BEGININTERIOR(ptr) +#ifdef _TARGET_X86_ + MetaSig msig(methodDesc); + if (msig.HasFPReturn()) { -#ifdef _DEBUG - BOOL GCOnTransition = FALSE; - if (g_pConfig->FastGCStressLevel()) { - GCOnTransition = GC_ON_TRANSITIONS (FALSE); - } -#endif - -#ifdef TIME_SUSPEND - g_SuspendStatistics.cntHijackTrap++; -#endif - - CommonTripThread(); -#ifdef _DEBUG - if (g_pConfig->FastGCStressLevel()) { - GC_ON_TRANSITIONS (GCOnTransition); - } -#endif - *(size_t*)&pArgs->ReturnValue = (size_t)ptr; + // Figuring out whether the function returns FP or not is hard to do + // on-the-fly, so we use a different callback helper on x86 where this + // piece of information is needed in order to perform the right save & + // restore of the return value around the call to OnHijackScalarWorker. + return RT_Float; } - GCPROTECT_END(); // trashes or here! - - frame.Pop(); -#else - PORTABILITY_ASSERT("OnHijackInteriorPointerWorker not implemented on this platform."); -#endif -} - -// A hijacked method is returning a scalar to its caller. Note that we bash the -// return address as an int on the stack. Since this is cdecl, our caller gets the -// bashed value. This is not intuitive for C programmers! -void STDCALL OnHijackScalarWorker(HijackArgs * pArgs) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - SO_TOLERANT; - } CONTRACTL_END; - -#ifdef HIJACK_NONINTERRUPTIBLE_THREADS - Thread *thread = GetThread(); +#endif // _TARGET_X86_ -#ifdef FEATURE_STACK_PROBE - if (GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) == eRudeUnloadAppDomain) + MethodTable* pMT = NULL; + MetaSig::RETURNTYPE type = methodDesc->ReturnsObject(INDEBUG_COMMA(false) &pMT); + if (type == MetaSig::RETOBJ) { - // Make sure default domain does not see SO. - // probe for our entry point amount and throw if not enough stack - RetailStackProbe(ADJUST_PROBE(DEFAULT_ENTRY_PROBE_AMOUNT), thread); + return RT_Object; } -#endif - CONTRACT_VIOLATION(SOToleranceViolation); - FastInterlockAnd((ULONG *) &thread->m_State, ~Thread::TS_Hijacked); - - // Fix up our caller's stack, so it can resume from the hijack correctly - pArgs->ReturnAddress = (size_t)thread->m_pvHJRetAddr; - - // Build a frame so that stack crawling can proceed from here back to where - // we will resume execution. - FrameWithCookie<HijackFrame> frame((void *)pArgs->ReturnAddress, thread, pArgs); - -#ifdef _DEBUG - BOOL GCOnTransition = FALSE; - if (g_pConfig->FastGCStressLevel()) { - GCOnTransition = GC_ON_TRANSITIONS (FALSE); + if (type == MetaSig::RETBYREF) + { + return RT_ByRef; } -#endif - -#ifdef TIME_SUSPEND - g_SuspendStatistics.cntHijackTrap++; -#endif - CommonTripThread(); -#ifdef _DEBUG - if (g_pConfig->FastGCStressLevel()) { - GC_ON_TRANSITIONS (GCOnTransition); +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + // The Multi-reg return case using the classhandle is only implemented for AMD64 SystemV ABI. + // On other platforms, multi-reg return is not supported with GcInfo v1. + // So, the relevant information must be obtained from the GcInfo tables (which requires version2). + if (type == MetaSig::RETVALUETYPE) + { + EEClass *eeClass = pMT->GetClass(); + ReturnKind regKinds[2] = { RT_Unset, RT_Unset }; + int orefCount = 0; + for (int i = 0; i < 2; i++) + { + if (eeClass->GetEightByteClassification(i) == SystemVClassificationTypeIntegerReference) + { + regKinds[i] = RT_Object; + } + else if (eeClass->GetEightByteClassification(i) == SystemVClassificationTypeIntegerByRef) + { + regKinds[i] = RT_ByRef; + } + else + { + regKinds[i] = RT_Scalar; + } + } + ReturnKind structReturnKind = GetStructReturnKind(regKinds[0], regKinds[1]); + return structReturnKind; } -#endif +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING - frame.Pop(); -#else - PORTABILITY_ASSERT("OnHijackScalarWorker not implemented on this platform."); -#endif + return RT_Scalar; } -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING -// A hijacked method is returning a struct in registers to its caller. -// The struct can possibly contain object references that we have to -// protect. -void STDCALL OnHijackStructInRegsWorker(HijackArgs * pArgs) +ReturnKind GetReturnKind(Thread *pThread, EECodeInfo *codeInfo) { - CONTRACTL { - THROWS; - GC_TRIGGERS; - SO_TOLERANT; - } CONTRACTL_END; + ReturnKind returnKind = RT_Illegal; -#ifdef HIJACK_NONINTERRUPTIBLE_THREADS - Thread *thread = GetThread(); - - EEClass* eeClass = thread->GetHijackReturnTypeClass(); - - OBJECTREF oref[CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS]; - int orefCount = 0; - for (int i = 0; i < eeClass->GetNumberEightBytes(); i++) +#ifdef _TARGET_X86_ + // X86 GCInfo updates yet to be implemented. +#else + GCInfoToken gcInfoToken = codeInfo->GetGCInfoToken(); + if (gcInfoToken.IsReturnKindAvailable()) { - if ((eeClass->GetEightByteClassification(i) == SystemVClassificationTypeIntegerReference) || - (eeClass->GetEightByteClassification(i) == SystemVClassificationTypeIntegerByRef)) - { - oref[orefCount++] = ObjectToOBJECTREF(*(Object **) &pArgs->ReturnValue[i]); - } + GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_RETURN_KIND); + ReturnKind returnKind = gcInfoDecoder.GetReturnKind(); } +#endif // _TARGET_X86_ -#ifdef FEATURE_STACK_PROBE - if (GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) == eRudeUnloadAppDomain) + if (!IsValidReturnKind(returnKind)) { - RetailStackProbe(ADJUST_PROBE(DEFAULT_ENTRY_PROBE_AMOUNT), thread); + returnKind = GetReturnKindFromMethodTable(pThread, codeInfo); } -#endif - - CONTRACT_VIOLATION(SOToleranceViolation); - - thread->ResetThreadState(Thread::TS_Hijacked); - - // Fix up our caller's stack, so it can resume from the hijack correctly - pArgs->ReturnAddress = (size_t)thread->m_pvHJRetAddr; - - // Build a frame so that stack crawling can proceed from here back to where - // we will resume execution. - FrameWithCookie<HijackFrame> frame((void *)pArgs->ReturnAddress, thread, pArgs); - - GCPROTECT_ARRAY_BEGIN(oref[0], orefCount) + else { -#ifdef _DEBUG - BOOL GCOnTransition = FALSE; - if (g_pConfig->FastGCStressLevel()) { - GCOnTransition = GC_ON_TRANSITIONS (FALSE); - } -#endif +#if !defined(FEATURE_MULTIREG_RETURN) || defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + // For ARM64 struct-return, GetReturnKindFromMethodTable() is not supported + _ASSERTE(returnKind == GetReturnKindFromMethodTable(pThread, codeInfo)); +#endif // !FEATURE_MULTIREG_RETURN || FEATURE_UNIX_AMD64_STRUCT_PASSING + } -#ifdef TIME_SUSPEND - g_SuspendStatistics.cntHijackTrap++; -#endif + _ASSERTE(IsValidReturnKind(returnKind)); + return returnKind; +} - CommonTripThread(); -#ifdef _DEBUG - if (g_pConfig->FastGCStressLevel()) { - GC_ON_TRANSITIONS (GCOnTransition); - } -#endif +VOID * GetHijackAddr(Thread *pThread, EECodeInfo *codeInfo) +{ + ReturnKind returnKind = GetReturnKind(pThread, codeInfo); + pThread->SetHijackReturnKind(returnKind); - // Update the references in the returned struct - orefCount = 0; - for (int i = 0; i < eeClass->GetNumberEightBytes(); i++) - { - if ((eeClass->GetEightByteClassification(i) == SystemVClassificationTypeIntegerReference) || - (eeClass->GetEightByteClassification(i) == SystemVClassificationTypeIntegerByRef)) - { - *((OBJECTREF *) &pArgs->ReturnValue[i]) = oref[orefCount++]; - } - } +#ifdef _TARGET_X86_ + if (returnKind == RT_Float) + { + return OnHijackFPTripThread; } - GCPROTECT_END(); +#endif // _TARGET_X86_ - frame.Pop(); -#else - PORTABILITY_ASSERT("OnHijackInteriorPointerWorker not implemented on this platform."); -#endif + return OnHijackTripThread; } -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING #ifndef PLATFORM_UNIX @@ -7801,11 +7709,12 @@ BOOL Thread::HandledJITCase(BOOL ForTaskSwitchIn) return FALSE; } - if (!ExecutionManager::IsManagedCode(GetIP(&ctx))) + PCODE ip = GetIP(&ctx); + if (!ExecutionManager::IsManagedCode(ip)) { return FALSE; } - + #ifdef WORKAROUND_RACES_WITH_KERNEL_MODE_EXCEPTION_HANDLING if (ThreadCaughtInKernelModeExceptionHandling(this, &ctx)) { @@ -7865,48 +7774,8 @@ BOOL Thread::HandledJITCase(BOOL ForTaskSwitchIn) // we need to hijack the return address. Base this on whether or not // the method returns an object reference, so we know whether to protect // it or not. - VOID *pvHijackAddr = OnHijackScalarTripThread; - if (esb.m_pFD) - { -#ifdef _WIN64 - // For simplicity, we don't hijack in funclets, but if you ever change that, - // be sure to choose the OnHijack... callback type to match that of the FUNCLET - // not the main method (it would probably be Scalar). -#endif // _WIN64 - - ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); - // Mark that we are performing a stackwalker like operation on the current thread. - // This is necessary to allow the signature parsing functions to work without triggering any loads - ClrFlsValueSwitch threadStackWalking(TlsIdx_StackWalkerWalkingThread, this); - -#ifdef _TARGET_X86_ - MetaSig msig(esb.m_pFD); - if (msig.HasFPReturn()) - { - // Figuring out whether the function returns FP or not is hard to do - // on-the-fly, so we use a different callback helper on x86 where this - // piece of information is needed in order to perform the right save & - // restore of the return value around the call to OnHijackScalarWorker. - pvHijackAddr = OnHijackFloatingPointTripThread; - } - else -#endif // _TARGET_X86_ - { - MethodTable* pMT = NULL; - MetaSig::RETURNTYPE type = esb.m_pFD->ReturnsObject(INDEBUG_COMMA(false) &pMT); - if (type == MetaSig::RETOBJ) - pvHijackAddr = OnHijackObjectTripThread; - else if (type == MetaSig::RETBYREF) - pvHijackAddr = OnHijackInteriorPointerTripThread; -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING - else if (type == MetaSig::RETVALUETYPE) - { - pThread->SetHijackReturnTypeClass(pMT->GetClass()); - pvHijackAddr = OnHijackStructInRegsTripThread; - } -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING - } - } + EECodeInfo codeInfo(ip); + VOID *pvHijackAddr = GetHijackAddr(this, &codeInfo); #ifdef FEATURE_ENABLE_GCPOLL // On platforms that support both hijacking and GC polling @@ -7919,7 +7788,6 @@ BOOL Thread::HandledJITCase(BOOL ForTaskSwitchIn) { HijackThread(pvHijackAddr, &esb); } - } } // else it's not even a JIT case @@ -8455,26 +8323,7 @@ void PALAPI HandleGCSuspensionForInterruptedThread(CONTEXT *interruptedContext) ClrFlsValueSwitch threadStackWalking(TlsIdx_StackWalkerWalkingThread, pThread); // Hijack the return address to point to the appropriate routine based on the method's return type. - void *pvHijackAddr = reinterpret_cast<void*>(OnHijackScalarTripThread); - MethodDesc *pMethodDesc = codeInfo.GetMethodDesc(); - MethodTable* pMT = NULL; - MetaSig::RETURNTYPE type = pMethodDesc->ReturnsObject(INDEBUG_COMMA(false) &pMT); - if (type == MetaSig::RETOBJ) - { - pvHijackAddr = reinterpret_cast<void*>(OnHijackObjectTripThread); - } - else if (type == MetaSig::RETBYREF) - { - pvHijackAddr = reinterpret_cast<void*>(OnHijackInteriorPointerTripThread); - } -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING - else if (type == MetaSig::RETVALUETYPE) - { - pThread->SetHijackReturnTypeClass(pMT->GetClass()); - pvHijackAddr = reinterpret_cast<void*>(OnHijackStructInRegsTripThread); - } -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING - + void *pvHijackAddr = GetHijackAddr(pThread, &codeInfo); pThread->HijackThread(pvHijackAddr, &executionState); } } |