Implement GcInfo v2
authorSwaroop Sridhar <swaroops@microsoft.com>
Wed, 20 Jul 2016 23:50:22 +0000 (16:50 -0700)
committerSwaroop Sridhar <swaroops@microsoft.com>
Fri, 5 Aug 2016 21:05:55 +0000 (14:05 -0700)
Ref #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 #6494 for ARM64.

No measurable change in JIT throughput, performance or native-image
size from this change.

30 files changed:
clrdefinitions.cmake
src/debug/daccess/nidump.cpp
src/gcdump/gcdumpnonx86.cpp
src/gcinfo/gcinfoencoder.cpp
src/inc/gcinfo.h
src/inc/gcinfodecoder.h
src/inc/gcinfoencoder.h
src/inc/gcinfotypes.h
src/jit/gcencode.cpp
src/vm/amd64/AsmHelpers.asm
src/vm/amd64/cgenamd64.cpp
src/vm/amd64/cgencpu.h
src/vm/amd64/unixasmhelpers.S
src/vm/arm/asmhelpers.S
src/vm/arm/asmhelpers.asm
src/vm/arm/cgencpu.h
src/vm/arm64/asmhelpers.asm
src/vm/codeman.cpp
src/vm/crossgencompile.cpp
src/vm/eetwain.cpp
src/vm/frames.cpp
src/vm/frames.h
src/vm/gcenv.ee.cpp
src/vm/gcinfodecoder.cpp
src/vm/i386/asmhelpers.asm
src/vm/i386/cgencpu.h
src/vm/i386/cgenx86.cpp
src/vm/threads.cpp
src/vm/threads.h
src/vm/threadsuspend.cpp

index 5ff9be4..20040b7 100644 (file)
@@ -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)
index d151a54..32eab49 100644 (file)
@@ -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
 
index 53e16ff..7343ac9 100644 (file)
@@ -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);
 
index 514a3c9..3e587ea 100644 (file)
@@ -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
 }
 
index 500e1b7..8d249a3 100644 (file)
@@ -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;
+    }
 };
 
 /*****************************************************************************/
index f703727..c77c359 100644 (file)
@@ -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);
 
index 8875d3b..838f1ba 100644 (file)
  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)
     - 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
index fc624b2..c08dd79 100644 (file)
@@ -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
index ec0eba3..1f9065e 100644 (file)
@@ -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)
     {
index 1522670..4563a06 100644 (file)
@@ -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
 
 
 ;
index d569800..1f7d20a 100644 (file)
@@ -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 {
index 409b8e5..258ac38 100644 (file)
@@ -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
index 7db8e52..837dcf9 100644 (file)
@@ -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
 
index d269186..55bdd9f 100644 (file)
@@ -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
 
index 283d837..3a6d446 100644 (file)
@@ -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}
index 30573eb..936fdab 100644 (file)
@@ -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()
     };
 
     //
index 89e6710..1369bd8 100644 (file)
@@ -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]
index 9ce6bb2..89084db 100644 (file)
@@ -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)
index 36ba0f6..85859c2 100644 (file)
@@ -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()
 {
 }
index 82b76f6..165335f 100644 (file)
@@ -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();
index eff32f3..b99c451 100644 (file)
@@ -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
index 7b55c36..0926f29 100644 (file)
@@ -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:
index da36e72..2f1e4e8 100644 (file)
@@ -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())
index 351e221..ef237a2 100644 (file)
@@ -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;
index dc3956c..66a22b7 100644 (file)
@@ -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
 
index d168429..2da9882 100644 (file)
@@ -513,7 +513,7 @@ struct HijackArgs
     union
     {
         DWORD Eax;
-        size_t ReturnValue;
+        size_t ReturnValue[1];
     };
     DWORD Ebp;
     union
index 48428a1..ff2f2df 100644 (file)
@@ -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 {
index d0f1900..e7f4ccb 100644 (file)
@@ -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.
 //--------------------------------------------------------------------
index e704c2b..bd81c63 100644 (file)
 #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
index 6d25012..2cf32df 100644 (file)
@@ -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);
     }
 }