From 293a19f4c000eae0f316f3f77a61b2c80b5703fc Mon Sep 17 00:00:00 2001 From: Swaroop Sridhar Date: Wed, 20 Jul 2016 16:50:22 -0700 Subject: [PATCH] Implement GcInfo v2 Ref dotnet/coreclr#4379 This change implements GcInfo version 2 for all platforms that use the GcInfo library (all architectures other than X86). Changes are: 1) Defines ReturnKind enumeration for all platforms 2) Change the GcInfo encoder library to encode the ReturnKind and ReversePInvokeFrame slot 3) Change the CM's GcInfo decoder to encode the ReturnKind and ReversePInvokeFrame slot for GCINFO_VERSION 2 4) Some corrections to GCINFO_MEASUREments 5) Changes to RYU Jit to provide the correct information to the encoder 6) Changes to the VM to use the ReturnKind information while hijacking a thread - If ReturnKind is available from GcInfo, new hijack routines are used - Otherwise, fall back to old method (for compatibility) 7) Rework and simplify the thread hijack routines by scanning HijackFrames directly for gcroots 8) Supporting code to implement the above features. Returning Structs in multiple registers Hijacking for StructInRegs is currently only implemented for Unix SystemV ABI Multi-reg struct returns. However, the hijack-workers that use ReturnKind are ready to handle other platforms (ex: ARM/ARM64 Windows) once the corresponding HijackTripThread() assembly routines are defined. The New feature flag: FEATURE_MULTIREG_RETURN is set for platforms where a struct value 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 specified by SystemV ABI for AMD64 Compatibility with other JITs - All new GCInfo generated by RYU Jit is in GcInfo version 2 - All Ngen images must be regenerated with the new GcInfo version. - Ready-to-run images with old GcInfo will continue to work. - Jit64/X64 uses the GcInfo library, so it generates GcInfo version 2. However, it doesn't (yet) provide the data to encode the correct ReturnKind Similar is the case for ARM32 code running on JIT32, and any other JITs that may be using GcInfo library but not yet modified to use the new API. So, compatibility is achived using RT_Unset flag. 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. Implement GC root scanning for Hijack-frames This change implements GCScanRoots() method for Hijacke-frames based on the ReturnKind information available from the GcInfo. If the exact ReturnKind is not available in the GcInfo, the thread-suspension logic will compute the ReturnKind based on the method-signature. As a result of this change, several hijack-helpers in the VM are cleaned up. There's only one implementation of HijackWorker() to handle all returnKinds. This change also simplifies the thread-hijack logic by using a single assembly helper OnHijackTripThread() in most cases. The only other helper used is for X86 floating point return values for save/restoring the top of the FP stack. ARM64 Only GcIndfo v2 is reliably supported for ARM64 platform. The changes to thread-hijack mechanism fixes dotnet/coreclr#6494 for ARM64. No measurable change in JIT throughput, performance or native-image size from this change. Commit migrated from https://github.com/dotnet/coreclr/commit/d302e64bcb4ea445f6702c529f8a964df20ab494 --- src/coreclr/clrdefinitions.cmake | 2 + src/coreclr/src/debug/daccess/nidump.cpp | 6 +- src/coreclr/src/gcdump/gcdumpnonx86.cpp | 6 +- src/coreclr/src/gcinfo/gcinfoencoder.cpp | 63 ++++- src/coreclr/src/inc/gcinfo.h | 26 +- src/coreclr/src/inc/gcinfodecoder.h | 17 +- src/coreclr/src/inc/gcinfoencoder.h | 28 ++- src/coreclr/src/inc/gcinfotypes.h | 218 ++++++++++++++++- src/coreclr/src/jit/gcencode.cpp | 60 ++++- src/coreclr/src/vm/amd64/AsmHelpers.asm | 58 +---- src/coreclr/src/vm/amd64/cgenamd64.cpp | 3 +- src/coreclr/src/vm/amd64/cgencpu.h | 6 +- src/coreclr/src/vm/amd64/unixasmhelpers.S | 110 +-------- src/coreclr/src/vm/arm/asmhelpers.S | 32 +-- src/coreclr/src/vm/arm/asmhelpers.asm | 23 +- src/coreclr/src/vm/arm/cgencpu.h | 6 +- src/coreclr/src/vm/arm64/asmhelpers.asm | 58 +---- src/coreclr/src/vm/codeman.cpp | 4 +- src/coreclr/src/vm/crossgencompile.cpp | 5 + src/coreclr/src/vm/eetwain.cpp | 24 +- src/coreclr/src/vm/frames.cpp | 54 +++++ src/coreclr/src/vm/frames.h | 7 +- src/coreclr/src/vm/gcenv.ee.cpp | 8 +- src/coreclr/src/vm/gcinfodecoder.cpp | 92 ++++++-- src/coreclr/src/vm/i386/asmhelpers.asm | 92 +------- src/coreclr/src/vm/i386/cgencpu.h | 2 +- src/coreclr/src/vm/i386/cgenx86.cpp | 3 +- src/coreclr/src/vm/threads.cpp | 5 +- src/coreclr/src/vm/threads.h | 64 ++--- src/coreclr/src/vm/threadsuspend.cpp | 381 +++++++++--------------------- 30 files changed, 739 insertions(+), 724 deletions(-) diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 5ff9be4..20040b7 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/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/coreclr/src/debug/daccess/nidump.cpp b/src/coreclr/src/debug/daccess/nidump.cpp index d151a54..32eab49 100644 --- a/src/coreclr/src/debug/daccess/nidump.cpp +++ b/src/coreclr/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/coreclr/src/gcdump/gcdumpnonx86.cpp b/src/coreclr/src/gcdump/gcdumpnonx86.cpp index 53e16ff..7343ac9 100644 --- a/src/coreclr/src/gcdump/gcdumpnonx86.cpp +++ b/src/coreclr/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/coreclr/src/gcinfo/gcinfoencoder.cpp b/src/coreclr/src/gcinfo/gcinfoencoder.cpp index 514a3c9..3e587ea 100644 --- a/src/coreclr/src/gcinfo/gcinfoencoder.cpp +++ b/src/coreclr/src/gcinfo/gcinfoencoder.cpp @@ -324,13 +324,17 @@ typedef SimplerHashTable(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/coreclr/src/inc/gcinfo.h b/src/coreclr/src/inc/gcinfo.h index 500e1b7..8d249a3 100644 --- a/src/coreclr/src/inc/gcinfo.h +++ b/src/coreclr/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/coreclr/src/inc/gcinfodecoder.h b/src/coreclr/src/inc/gcinfodecoder.h index f703727..c77c359 100644 --- a/src/coreclr/src/inc/gcinfodecoder.h +++ b/src/coreclr/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/coreclr/src/inc/gcinfoencoder.h b/src/coreclr/src/inc/gcinfoencoder.h index 8875d3b..838f1ba 100644 --- a/src/coreclr/src/inc/gcinfoencoder.h +++ b/src/coreclr/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/coreclr/src/inc/gcinfotypes.h b/src/coreclr/src/inc/gcinfotypes.h index fc624b2..c08dd79 100644 --- a/src/coreclr/src/inc/gcinfotypes.h +++ b/src/coreclr/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: + // + // 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 ""; + default: return "!Impossible!"; + } +} + #ifdef _TARGET_X86_ #include // 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/coreclr/src/jit/gcencode.cpp b/src/coreclr/src/jit/gcencode.cpp index ec0eba3..1f9065e 100644 --- a/src/coreclr/src/jit/gcencode.cpp +++ b/src/coreclr/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/coreclr/src/vm/amd64/AsmHelpers.asm b/src/coreclr/src/vm/amd64/AsmHelpers.asm index 1522670..4563a06 100644 --- a/src/coreclr/src/vm/amd64/AsmHelpers.asm +++ b/src/coreclr/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/coreclr/src/vm/amd64/cgenamd64.cpp b/src/coreclr/src/vm/amd64/cgenamd64.cpp index d569800..1f7d20a 100644 --- a/src/coreclr/src/vm/amd64/cgenamd64.cpp +++ b/src/coreclr/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/coreclr/src/vm/amd64/cgencpu.h b/src/coreclr/src/vm/amd64/cgencpu.h index 409b8e5..258ac38 100644 --- a/src/coreclr/src/vm/amd64/cgencpu.h +++ b/src/coreclr/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/coreclr/src/vm/amd64/unixasmhelpers.S b/src/coreclr/src/vm/amd64/unixasmhelpers.S index 7db8e52..837dcf9 100644 --- a/src/coreclr/src/vm/amd64/unixasmhelpers.S +++ b/src/coreclr/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/coreclr/src/vm/arm/asmhelpers.S b/src/coreclr/src/vm/arm/asmhelpers.S index d269186..55bdd9f 100644 --- a/src/coreclr/src/vm/arm/asmhelpers.S +++ b/src/coreclr/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/coreclr/src/vm/arm/asmhelpers.asm b/src/coreclr/src/vm/arm/asmhelpers.asm index 283d837..3a6d446 100644 --- a/src/coreclr/src/vm/arm/asmhelpers.asm +++ b/src/coreclr/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/coreclr/src/vm/arm/cgencpu.h b/src/coreclr/src/vm/arm/cgencpu.h index 30573eb..936fdab 100644 --- a/src/coreclr/src/vm/arm/cgencpu.h +++ b/src/coreclr/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/coreclr/src/vm/arm64/asmhelpers.asm b/src/coreclr/src/vm/arm64/asmhelpers.asm index 89e6710..1369bd8 100644 --- a/src/coreclr/src/vm/arm64/asmhelpers.asm +++ b/src/coreclr/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/coreclr/src/vm/codeman.cpp b/src/coreclr/src/vm/codeman.cpp index 9ce6bb2..89084db 100644 --- a/src/coreclr/src/vm/codeman.cpp +++ b/src/coreclr/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/coreclr/src/vm/crossgencompile.cpp b/src/coreclr/src/vm/crossgencompile.cpp index 36ba0f6..85859c2 100644 --- a/src/coreclr/src/vm/crossgencompile.cpp +++ b/src/coreclr/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/coreclr/src/vm/eetwain.cpp b/src/coreclr/src/vm/eetwain.cpp index 82b76f6..165335f 100644 --- a/src/coreclr/src/vm/eetwain.cpp +++ b/src/coreclr/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/coreclr/src/vm/frames.cpp b/src/coreclr/src/vm/frames.cpp index eff32f3..b99c451 100644 --- a/src/coreclr/src/vm/frames.cpp +++ b/src/coreclr/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(&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/coreclr/src/vm/frames.h b/src/coreclr/src/vm/frames.h index 7b55c36..0926f29 100644 --- a/src/coreclr/src/vm/frames.h +++ b/src/coreclr/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/coreclr/src/vm/gcenv.ee.cpp b/src/coreclr/src/vm/gcenv.ee.cpp index da36e72..2f1e4e8 100644 --- a/src/coreclr/src/vm/gcenv.ee.cpp +++ b/src/coreclr/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/coreclr/src/vm/gcinfodecoder.cpp b/src/coreclr/src/vm/gcinfodecoder.cpp index 351e221..ef237a2 100644 --- a/src/coreclr/src/vm/gcinfodecoder.cpp +++ b/src/coreclr/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(gcInfoToken.Info)) , m_InstructionOffset(breakOffset) , m_IsInterruptible(false) + , m_ReturnKind(RT_Illegal) #ifdef _DEBUG , m_Flags( flags ) , m_GcInfoAddress(dac_cast(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/coreclr/src/vm/i386/asmhelpers.asm b/src/coreclr/src/vm/i386/asmhelpers.asm index dc3956c..66a22b7 100644 --- a/src/coreclr/src/vm/i386/asmhelpers.asm +++ b/src/coreclr/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/coreclr/src/vm/i386/cgencpu.h b/src/coreclr/src/vm/i386/cgencpu.h index d168429..2da9882 100644 --- a/src/coreclr/src/vm/i386/cgencpu.h +++ b/src/coreclr/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/coreclr/src/vm/i386/cgenx86.cpp b/src/coreclr/src/vm/i386/cgenx86.cpp index 48428a1..ff2f2df 100644 --- a/src/coreclr/src/vm/i386/cgenx86.cpp +++ b/src/coreclr/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/coreclr/src/vm/threads.cpp b/src/coreclr/src/vm/threads.cpp index d0f1900..e7f4ccb 100644 --- a/src/coreclr/src/vm/threads.cpp +++ b/src/coreclr/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/coreclr/src/vm/threads.h b/src/coreclr/src/vm/threads.h index e704c2b..bd81c63 100644 --- a/src/coreclr/src/vm/threads.h +++ b/src/coreclr/src/vm/threads.h @@ -143,6 +143,7 @@ #include "mscoree.h" #include "appdomainstack.h" #include "gc.h" +#include "gcinfotypes.h" #include 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/coreclr/src/vm/threadsuspend.cpp b/src/coreclr/src/vm/threadsuspend.cpp index 6d25012..2cf32dfb 100644 --- a/src/coreclr/src/vm/threadsuspend.cpp +++ b/src/coreclr/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 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 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 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 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(OnHijackScalarTripThread); - MethodDesc *pMethodDesc = codeInfo.GetMethodDesc(); - MethodTable* pMT = NULL; - MetaSig::RETURNTYPE type = pMethodDesc->ReturnsObject(INDEBUG_COMMA(false) &pMT); - if (type == MetaSig::RETOBJ) - { - pvHijackAddr = reinterpret_cast(OnHijackObjectTripThread); - } - else if (type == MetaSig::RETBYREF) - { - pvHijackAddr = reinterpret_cast(OnHijackInteriorPointerTripThread); - } -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING - else if (type == MetaSig::RETVALUETYPE) - { - pThread->SetHijackReturnTypeClass(pMT->GetClass()); - pvHijackAddr = reinterpret_cast(OnHijackStructInRegsTripThread); - } -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING - + void *pvHijackAddr = GetHijackAddr(pThread, &codeInfo); pThread->HijackThread(pvHijackAddr, &executionState); } } -- 2.7.4