Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit ea4efbb

Browse files
author
Swaroop Sridhar
committed
Implement GcInfo v2
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) 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.
1 parent 939c9a0 commit ea4efbb

18 files changed

+972
-144
lines changed

clrdefinitions.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ add_definitions(-DFEATURE_SVR_GC)
166166
add_definitions(-DFEATURE_SYMDIFF)
167167
add_definitions(-DFEATURE_SYNTHETIC_CULTURES)
168168
if(CLR_CMAKE_PLATFORM_UNIX_AMD64)
169+
add_definitions(-DFEATURE_MULTIREG_RETURN)
169170
add_definitions(-DFEATURE_UNIX_AMD64_STRUCT_PASSING)
170171
add_definitions(-DFEATURE_UNIX_AMD64_STRUCT_PASSING_ITF)
171172
endif (CLR_CMAKE_PLATFORM_UNIX_AMD64)

src/debug/daccess/nidump.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3112,7 +3112,7 @@ void NativeImageDumper::DumpCompleteMethod(PTR_Module module, MethodIterator& mi
31123112
GCDump gcDump(gcInfoToken.Version);
31133113
gcDump.gcPrintf = stringOutFn;
31143114
#if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER)
3115-
GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_CODE_LENGTH, 0);
3115+
GcInfoDecoder gcInfoDecoder(gcInfoToken, DECODE_CODE_LENGTH);
31163116
methodSize = gcInfoDecoder.GetCodeLength();
31173117
#endif
31183118

@@ -9440,7 +9440,7 @@ void NativeImageDumper::DumpReadyToRunMethod(PCODE pEntryPoint, PTR_RUNTIME_FUNC
94409440
GCDump gcDump(GCINFO_VERSION);
94419441
gcDump.gcPrintf = stringOutFn;
94429442
#if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER)
9443-
GcInfoDecoder gcInfoDecoder({ curGCInfoPtr, GCINFO_VERSION }, DECODE_CODE_LENGTH, 0);
9443+
GcInfoDecoder gcInfoDecoder({ curGCInfoPtr, GCINFO_VERSION }, DECODE_CODE_LENGTH);
94449444
methodSize = gcInfoDecoder.GetCodeLength();
94459445
#endif
94469446

src/gcdump/gcdumpnonx86.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock,
284284
| DECODE_VARARG
285285
| DECODE_GENERICS_INST_CONTEXT
286286
| DECODE_GC_LIFETIMES
287-
| DECODE_PROLOG_LENGTH),
287+
| DECODE_PROLOG_LENGTH
288+
| DECODE_RETURN_KIND),
288289
0);
289290

290291
if (NO_SECURITY_OBJECT != hdrdecoder.GetSecurityObjectStackSlot() ||
@@ -438,6 +439,9 @@ size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock,
438439
gcPrintf("Size of parameter area: %x\n", hdrdecoder.GetSizeOfStackParameterArea());
439440
#endif
440441

442+
ReturnKind returnKind = hdrdecoder.GetReturnKind();
443+
gcPrintf("Code size: %s\n", ReturnKindToString(returnKind));
444+
441445
UINT32 cbEncodedMethodSize = hdrdecoder.GetCodeLength();
442446
gcPrintf("Code size: %x\n", cbEncodedMethodSize);
443447

src/gcinfo/gcinfoencoder.cpp

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -324,13 +324,17 @@ typedef SimplerHashTable<const BitArray *, LiveStateFuncs, UINT32, GcInfoHashBeh
324324
// Pi = partially-interruptible; methods with zero fully-interruptible ranges
325325
GcInfoSize g_FiGcInfoSize;
326326
GcInfoSize g_PiGcInfoSize;
327+
// Number of methods with GcInfo that have SlimHeader
328+
size_t g_NumSlimHeaders = 0;
329+
// Number of methods with GcInfo that have FatHeader
330+
size_t g_NumFatHeaders = 0;
327331

328332
GcInfoSize::GcInfoSize()
329333
{
330334
memset(this, 0, sizeof(*this));
331335
}
332336

333-
GcInfoSize& operator+=(const GcInfoSize& other)
337+
GcInfoSize& GcInfoSize::operator+=(const GcInfoSize& other)
334338
{
335339
TotalSize += other.TotalSize;
336340

@@ -351,7 +355,7 @@ GcInfoSize& operator+=(const GcInfoSize& other)
351355
GenericsCtxSize += other.GenericsCtxSize;
352356
PspSymSize += other.PspSymSize;
353357
StackBaseSize += other.StackBaseSize;
354-
FrameMarkerSize += other.FrameMarkerSize;
358+
ReversePInvokeFrameSize += other.ReversePInvokeFrameSize;
355359
FixedAreaSize += other.FixedAreaSize;
356360
NumCallSitesSize += other.NumCallSitesSize;
357361
NumRangesSize += other.NumRangesSize;
@@ -398,8 +402,9 @@ void GcInfoSize::Log(DWORD level, const char * header)
398402
LogSpew(LF_GCINFO, level, "GsCookie: %Iu\n", GsCookieSize);
399403
LogSpew(LF_GCINFO, level, "PspSym: %Iu\n", PspSymSize);
400404
LogSpew(LF_GCINFO, level, "GenericsCtx: %Iu\n", GenericsCtxSize);
401-
LogSpew(LF_GCINFO, level, "FrameMarker: %Iu\n", FrameMarkerSize);
405+
LogSpew(LF_GCINFO, level, "StackBase: %Iu\n", StackBaseSize);
402406
LogSpew(LF_GCINFO, level, "FixedArea: %Iu\n", FixedAreaSize);
407+
LogSpew(LF_GCINFO, level, "ReversePInvokeFrame: %Iu\n", ReversePInvokeFrameSize);
403408
LogSpew(LF_GCINFO, level, "NumCallSites: %Iu\n", NumCallSitesSize);
404409
LogSpew(LF_GCINFO, level, "NumRanges: %Iu\n", NumRangesSize);
405410
LogSpew(LF_GCINFO, level, "CallSiteOffsets: %Iu\n", CallSitePosSize);
@@ -488,17 +493,27 @@ GcInfoEncoder::GcInfoEncoder(
488493

489494
m_StackBaseRegister = NO_STACK_BASE_REGISTER;
490495
m_SizeOfEditAndContinuePreservedArea = NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA;
496+
m_ReversePInvokeFrameSlot = NO_REVERSE_PINVOKE_FRAME;
491497
m_WantsReportOnlyLeaf = false;
492498
m_IsVarArg = false;
493499
m_pLastInterruptibleRange = NULL;
494500

495501
#ifdef _DEBUG
496502
m_IsSlotTableFrozen = FALSE;
503+
#endif //_DEBUG
504+
505+
#ifndef _TARGET_X86_
506+
// If the compiler doesn't set the GCInfo, report RT_Unset.
507+
// This is used for compatibility with JITs that aren't updated to use the new API.
508+
m_ReturnKind = RT_Unset;
509+
#else
510+
m_ReturnKind = RT_Illegal;
511+
#endif _TARGET_X86_
497512
m_CodeLength = 0;
498513
#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
499514
m_SizeOfStackOutgoingAndScratchArea = -1;
500515
#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA
501-
#endif //_DEBUG
516+
502517
}
503518

504519
#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
@@ -758,14 +773,27 @@ void GcInfoEncoder::SetSizeOfStackOutgoingAndScratchArea( UINT32 size )
758773
}
759774
#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA
760775

776+
void GcInfoEncoder::SetReversePInvokeFrameSlot(INT32 spOffset)
777+
{
778+
m_ReversePInvokeFrameSlot = spOffset;
779+
}
780+
781+
void GcInfoEncoder::SetReturnKind(ReturnKind returnKind)
782+
{
783+
_ASSERTE(returnKind != RT_Illegal);
784+
#ifndef _TARGET_X86_
785+
_ASSERTE(returnKind != RT_Unset);
786+
#endif // ! _TARGET_X86_
787+
788+
m_ReturnKind = returnKind;
789+
}
761790

762791
struct GcSlotDescAndId
763792
{
764793
GcSlotDesc m_SlotDesc;
765794
UINT32 m_SlotId;
766795
};
767796

768-
769797
int __cdecl CompareSlotDescAndIdBySlotDesc(const void* p1, const void* p2)
770798
{
771799
const GcSlotDesc* pFirst = &reinterpret_cast<const GcSlotDescAndId*>(p1)->m_SlotDesc;
@@ -985,20 +1013,26 @@ void GcInfoEncoder::Build()
9851013
// Method header
9861014
///////////////////////////////////////////////////////////////////////
9871015

1016+
9881017
UINT32 hasSecurityObject = (m_SecurityObjectStackSlot != NO_SECURITY_OBJECT);
9891018
UINT32 hasGSCookie = (m_GSCookieStackSlot != NO_GS_COOKIE);
9901019
UINT32 hasContextParamType = (m_GenericsInstContextStackSlot != NO_GENERICS_INST_CONTEXT);
1020+
UINT32 hasReversePInvokeFrame = (m_ReversePInvokeFrameSlot != NO_REVERSE_PINVOKE_FRAME);
9911021

9921022
BOOL slimHeader = (!m_IsVarArg && !hasSecurityObject && !hasGSCookie && (m_PSPSymStackSlot == NO_PSP_SYM) &&
993-
!hasContextParamType && !m_WantsReportOnlyLeaf && (m_InterruptibleRanges.Count() == 0) &&
1023+
!hasContextParamType && !m_WantsReportOnlyLeaf && (m_InterruptibleRanges.Count() == 0) && !hasReversePInvokeFrame &&
9941024
((m_StackBaseRegister == NO_STACK_BASE_REGISTER) || (NORMALIZE_STACK_BASE_REGISTER(m_StackBaseRegister) == 0))) &&
9951025
(m_SizeOfEditAndContinuePreservedArea == NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA);
9961026

1027+
// All new code is generated for the latest GCINFO_VERSION.
1028+
// So, always encode RetunrKind and encode ReversePInvokeFrameSlot where applicable.
9971029
if (slimHeader)
9981030
{
9991031
// Slim encoding means nothing special, partially interruptible, maybe a default frame register
10001032
GCINFO_WRITE(m_Info1, 0, 1, FlagsSize); // Slim encoding
10011033
GCINFO_WRITE(m_Info1, (m_StackBaseRegister == NO_STACK_BASE_REGISTER) ? 0 : 1, 1, FlagsSize);
1034+
1035+
GCINFO_WRITE(m_Info1, m_ReturnKind, SIZE_OF_RETURN_KIND_IN_SLIM_HEADER, RetKindSize);
10021036
}
10031037
else
10041038
{
@@ -1011,6 +1045,9 @@ void GcInfoEncoder::Build()
10111045
GCINFO_WRITE(m_Info1, ((m_StackBaseRegister != NO_STACK_BASE_REGISTER) ? 1 : 0), 1, FlagsSize);
10121046
GCINFO_WRITE(m_Info1, (m_WantsReportOnlyLeaf ? 1 : 0), 1, FlagsSize);
10131047
GCINFO_WRITE(m_Info1, ((m_SizeOfEditAndContinuePreservedArea != NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA) ? 1 : 0), 1, FlagsSize);
1048+
GCINFO_WRITE(m_Info1, (hasReversePInvokeFrame ? 1 : 0), 1, FlagsSize);
1049+
1050+
GCINFO_WRITE(m_Info1, m_ReturnKind, SIZE_OF_RETURN_KIND_IN_FAT_HEADER, RetKindSize);
10141051
}
10151052

10161053
_ASSERTE( m_CodeLength > 0 );
@@ -1109,6 +1146,12 @@ void GcInfoEncoder::Build()
11091146
GCINFO_WRITE_VARL_U(m_Info1, m_SizeOfEditAndContinuePreservedArea, SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE, EncPreservedSlots);
11101147
}
11111148

1149+
if (hasReversePInvokeFrame)
1150+
{
1151+
_ASSERTE(!slimHeader);
1152+
GCINFO_WRITE_VARL_S(m_Info1, NORMALIZE_STACK_SLOT(m_ReversePInvokeFrameSlot), REVERSE_PINVOKE_FRAME_ENCBASE, ReversePInvokeFrameSize);
1153+
}
1154+
11121155
#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
11131156
if (!slimHeader)
11141157
{
@@ -2305,6 +2348,15 @@ lExitSuccess:;
23052348
//-------------------------------------------------------------------
23062349

23072350
#ifdef MEASURE_GCINFO
2351+
if (slimHeader)
2352+
{
2353+
g_NumSlimHeaders++;
2354+
}
2355+
else
2356+
{
2357+
g_NumFatHeaders++;
2358+
}
2359+
23082360
m_CurrentMethodSize.NumMethods = 1;
23092361
#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
23102362
m_CurrentMethodSize.NumCallSites = m_NumCallSites;
@@ -2331,6 +2383,8 @@ lExitSuccess:;
23312383
m_CurrentMethodSize.Log(LL_INFO100, "=== PartiallyInterruptible method breakdown ===\r\n");
23322384
g_PiGcInfoSize.Log(LL_INFO10, "=== PartiallyInterruptible global breakdown ===\r\n");
23332385
}
2386+
LogSpew(LF_GCINFO, LL_INFO10, "Total SlimHeaders: %Iu\n", g_NumSlimHeaders);
2387+
LogSpew(LF_GCINFO, LL_INFO10, "NumMethods: %Iu\n", g_NumFatHeaders);
23342388
#endif
23352389
}
23362390

src/inc/gcinfo.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,15 @@ const unsigned this_OFFSET_FLAG = 0x2; // the offset is "this"
3232
// The current GCInfo Version
3333
//-----------------------------------------------------------------------------
3434

35+
#ifdef _TARGET_X86_
36+
// X86 GcInfo encoding is yet to be changed.
3537
#define GCINFO_VERSION 1
38+
#else
39+
#define GCINFO_VERSION 2
40+
#endif // _TARGET_X86_
3641

42+
#define MIN_GCINFO_VERSION_WITH_RETURN_KIND 2
43+
#define MIN_GCINFO_VERSION_WITH_REV_PINVOKE_FRAME 2
3744
//-----------------------------------------------------------------------------
3845
// GCInfoToken: A wrapper that contains the GcInfo data and version number.
3946
//
@@ -45,14 +52,22 @@ const unsigned this_OFFSET_FLAG = 0x2; // the offset is "this"
4552
// 1) The current GCINFO_VERSION for JITted and Ngened images
4653
// 2) A function of the Ready - to - run major version stored in READYTORUN_HEADER
4754
// for ready - to - run images.ReadyToRunJitManager::JitTokenToGCInfoVersion()
48-
// provides the GcInfo version for any Method.Currently, there's only one
49-
// version of GCInfo.
55+
// provides the GcInfo version for any Method.
5056
//-----------------------------------------------------------------------------
5157

5258
struct GCInfoToken
5359
{
5460
PTR_VOID Info;
5561
UINT32 Version;
62+
63+
BOOL IsReturnKindAvailable()
64+
{
65+
return (Version >= MIN_GCINFO_VERSION_WITH_RETURN_KIND);
66+
}
67+
BOOL IsReversePInvokeFrameAvailable()
68+
{
69+
return (Version >= MIN_GCINFO_VERSION_WITH_REV_PINVOKE_FRAME);
70+
}
5671
};
5772

5873
/*****************************************************************************/

src/inc/gcinfodecoder.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ enum ICodeManagerFlags
161161

162162
enum GcInfoDecoderFlags
163163
{
164+
DECODE_EVERYTHING = 0x0,
164165
DECODE_SECURITY_OBJECT = 0x01, // stack location of security object
165166
DECODE_CODE_LENGTH = 0x02,
166167
DECODE_VARARG = 0x04,
@@ -174,6 +175,8 @@ enum GcInfoDecoderFlags
174175
DECODE_FOR_RANGES_CALLBACK = 0x200,
175176
DECODE_PROLOG_LENGTH = 0x400, // length of the prolog (used to avoid reporting generics context)
176177
DECODE_EDIT_AND_CONTINUE = 0x800,
178+
DECODE_REVERSE_PINVOKE_VAR = 0x1000,
179+
DECODE_RETURN_KIND = 0x2000
177180
};
178181

179182
enum GcInfoHeaderFlags
@@ -190,11 +193,12 @@ enum GcInfoHeaderFlags
190193
GC_INFO_HAS_STACK_BASE_REGISTER = 0x40,
191194
GC_INFO_WANTS_REPORT_ONLY_LEAF = 0x80,
192195
GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS = 0x100,
196+
GC_INFO_REVERSE_PINVOKE_FRAME = 0x200,
193197

194-
GC_INFO_FLAGS_BIT_SIZE = 9,
198+
GC_INFO_FLAGS_BIT_SIZE_VERSION_1 = 9,
199+
GC_INFO_FLAGS_BIT_SIZE = 10,
195200
};
196201

197-
198202
class BitStreamReader
199203
{
200204
public:
@@ -430,7 +434,7 @@ class GcInfoDecoder
430434
// If you are not insterested in interruptibility or gc lifetime information, pass 0 as instructionOffset
431435
GcInfoDecoder(
432436
GCInfoToken gcInfoToken,
433-
GcInfoDecoderFlags flags,
437+
GcInfoDecoderFlags flags = DECODE_EVERYTHING,
434438
UINT32 instructionOffset = 0
435439
);
436440

@@ -486,10 +490,12 @@ class GcInfoDecoder
486490
UINT32 GetPrologSize();
487491
INT32 GetPSPSymStackSlot();
488492
INT32 GetGenericsInstContextStackSlot();
493+
INT32 GetReversePInvokeStackSlot();
489494
bool HasMethodDescGenericsInstContext();
490495
bool HasMethodTableGenericsInstContext();
491496
bool GetIsVarArg();
492497
bool WantsReportOnlyLeaf();
498+
ReturnKind GetReturnKind();
493499
UINT32 GetCodeLength();
494500
UINT32 GetStackBaseRegister();
495501
UINT32 GetSizeOfEditAndContinuePreservedArea();
@@ -512,13 +518,16 @@ class GcInfoDecoder
512518
bool m_WantsReportOnlyLeaf;
513519
INT32 m_SecurityObjectStackSlot;
514520
INT32 m_GSCookieStackSlot;
521+
INT32 m_ReversePInvokeStackSlot;
515522
UINT32 m_ValidRangeStart;
516523
UINT32 m_ValidRangeEnd;
517524
INT32 m_PSPSymStackSlot;
518525
INT32 m_GenericsInstContextStackSlot;
519526
UINT32 m_CodeLength;
520527
UINT32 m_StackBaseRegister;
521528
UINT32 m_SizeOfEditAndContinuePreservedArea;
529+
INT32 m_ReversePInvokeFrameSlot;
530+
ReturnKind m_ReturnKind;
522531
#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
523532
UINT32 m_NumSafePoints;
524533
UINT32 m_SafePointIndex;

0 commit comments

Comments
 (0)