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