in src/coreclr/gcinfo/gcinfoencoder.cpp [1016:2309]
void GcInfoEncoder::Build()
{
#ifdef _DEBUG
_ASSERTE(m_IsSlotTableFrozen || m_NumSlots == 0);
_ASSERTE((1 << NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2) == NUM_NORM_CODE_OFFSETS_PER_CHUNK);
char methodName[256];
m_pCorJitInfo->printMethodName(m_pMethodInfo->ftn, methodName, sizeof(methodName));
char className[256];
m_pCorJitInfo->printClassName(m_pCorJitInfo->getMethodClass(m_pMethodInfo->ftn), className, sizeof(className));
LOG((LF_GCINFO, LL_INFO100,
"Entering GcInfoEncoder::Build() for method %s:%s\n",
className, methodName));
#endif
///////////////////////////////////////////////////////////////////////
// Method header
///////////////////////////////////////////////////////////////////////
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 && !hasGSCookie && (m_PSPSymStackSlot == NO_PSP_SYM) &&
!hasContextParamType && (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) &&
#ifdef TARGET_AMD64
!m_WantsReportOnlyLeaf &&
#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
!m_HasTailCalls &&
#endif // TARGET_AMD64
!IsStructReturnKind(m_ReturnKind);
// 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
#if defined(TARGET_LOONGARCH64)
assert(m_StackBaseRegister == 22 || 3 == m_StackBaseRegister);
#elif defined(TARGET_RISCV64)
assert(m_StackBaseRegister == 8 || 2 == m_StackBaseRegister);
#endif
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
{
GCINFO_WRITE(m_Info1, 1, 1, FlagsSize); // Fat encoding
GCINFO_WRITE(m_Info1, (m_IsVarArg ? 1 : 0), 1, FlagsSize);
GCINFO_WRITE(m_Info1, 0 /* unused - was hasSecurityObject */, 1, FlagsSize);
GCINFO_WRITE(m_Info1, (hasGSCookie ? 1 : 0), 1, FlagsSize);
GCINFO_WRITE(m_Info1, ((m_PSPSymStackSlot != NO_PSP_SYM) ? 1 : 0), 1, FlagsSize);
GCINFO_WRITE(m_Info1, m_contextParamType, 2, FlagsSize);
#if defined(TARGET_LOONGARCH64)
assert(m_StackBaseRegister == 22 || 3 == m_StackBaseRegister);
#elif defined(TARGET_RISCV64)
assert(m_StackBaseRegister == 8 || 2 == m_StackBaseRegister);
#endif
GCINFO_WRITE(m_Info1, ((m_StackBaseRegister != NO_STACK_BASE_REGISTER) ? 1 : 0), 1, FlagsSize);
#ifdef TARGET_AMD64
GCINFO_WRITE(m_Info1, (m_WantsReportOnlyLeaf ? 1 : 0), 1, FlagsSize);
#elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
GCINFO_WRITE(m_Info1, (m_HasTailCalls ? 1 : 0), 1, FlagsSize);
#endif // TARGET_AMD64
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 );
_ASSERTE(DENORMALIZE_CODE_LENGTH(NORMALIZE_CODE_LENGTH(m_CodeLength)) == m_CodeLength);
GCINFO_WRITE_VARL_U(m_Info1, NORMALIZE_CODE_LENGTH(m_CodeLength), CODE_LENGTH_ENCBASE, CodeLengthSize);
if(hasGSCookie)
{
_ASSERTE(!slimHeader);
// Save the valid code range, to be used for determining when GS cookie validation
// should be performed
// Encode an intersection of valid offsets
UINT32 intersectionStart = m_GSCookieValidRangeStart;
UINT32 intersectionEnd = m_GSCookieValidRangeEnd;
_ASSERTE(intersectionStart > 0 && intersectionStart < m_CodeLength);
_ASSERTE(intersectionEnd > 0 && intersectionEnd <= m_CodeLength);
_ASSERTE(intersectionStart <= intersectionEnd);
UINT32 normPrologSize = NORMALIZE_CODE_OFFSET(intersectionStart);
UINT32 normEpilogSize = NORMALIZE_CODE_OFFSET(m_CodeLength) - NORMALIZE_CODE_OFFSET(intersectionEnd);
_ASSERTE(normPrologSize > 0 && normPrologSize < m_CodeLength);
_ASSERTE(normEpilogSize < m_CodeLength);
GCINFO_WRITE_VARL_U(m_Info1, normPrologSize-1, NORM_PROLOG_SIZE_ENCBASE, ProEpilogSize);
GCINFO_WRITE_VARL_U(m_Info1, normEpilogSize, NORM_EPILOG_SIZE_ENCBASE, ProEpilogSize);
}
else if (hasContextParamType)
{
_ASSERTE(!slimHeader);
// Save the prolog size, to be used for determining when it is not safe
// to report generics param context and the security object
_ASSERTE(m_GSCookieValidRangeStart > 0 && m_GSCookieValidRangeStart < m_CodeLength);
UINT32 normPrologSize = NORMALIZE_CODE_OFFSET(m_GSCookieValidRangeStart);
_ASSERTE(normPrologSize > 0 && normPrologSize < m_CodeLength);
GCINFO_WRITE_VARL_U(m_Info1, normPrologSize-1, NORM_PROLOG_SIZE_ENCBASE, ProEpilogSize);
}
// Encode the offset to the GS cookie.
if(hasGSCookie)
{
_ASSERTE(!slimHeader);
#ifdef _DEBUG
LOG((LF_GCINFO, LL_INFO1000, "GS cookie at " FMT_STK "\n",
DBG_STK(m_GSCookieStackSlot)
));
#endif
GCINFO_WRITE_VARL_S(m_Info1, NORMALIZE_STACK_SLOT(m_GSCookieStackSlot), GS_COOKIE_STACK_SLOT_ENCBASE, GsCookieSize);
}
// Encode the offset to the PSPSym.
// The PSPSym is relative to the caller SP on IA64 and the initial stack pointer before stack allocations on X64.
if(m_PSPSymStackSlot != NO_PSP_SYM)
{
_ASSERTE(!slimHeader);
#ifdef _DEBUG
LOG((LF_GCINFO, LL_INFO1000, "Parent PSP at " FMT_STK "\n", DBG_STK(m_PSPSymStackSlot)));
#endif
GCINFO_WRITE_VARL_S(m_Info1, NORMALIZE_STACK_SLOT(m_PSPSymStackSlot), PSP_SYM_STACK_SLOT_ENCBASE, PspSymSize);
}
// Encode the offset to the generics type context.
if(m_GenericsInstContextStackSlot != NO_GENERICS_INST_CONTEXT)
{
_ASSERTE(!slimHeader);
#ifdef _DEBUG
LOG((LF_GCINFO, LL_INFO1000, "Generics instantiation context at " FMT_STK "\n",
DBG_STK(m_GenericsInstContextStackSlot)
));
#endif
GCINFO_WRITE_VARL_S(m_Info1, NORMALIZE_STACK_SLOT(m_GenericsInstContextStackSlot), GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE, GenericsCtxSize);
}
if(!slimHeader && (m_StackBaseRegister != NO_STACK_BASE_REGISTER))
{
#if defined(TARGET_LOONGARCH64)
assert(m_StackBaseRegister == 22 || 3 == m_StackBaseRegister);
#elif defined(TARGET_RISCV64)
assert(m_StackBaseRegister == 8 || 2 == m_StackBaseRegister);
#endif
GCINFO_WRITE_VARL_U(m_Info1, NORMALIZE_STACK_BASE_REGISTER(m_StackBaseRegister), STACK_BASE_REGISTER_ENCBASE, StackBaseSize);
}
if (m_SizeOfEditAndContinuePreservedArea != NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA)
{
GCINFO_WRITE_VARL_U(m_Info1, m_SizeOfEditAndContinuePreservedArea, SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE, EncInfoSize);
#ifdef TARGET_ARM64
GCINFO_WRITE_VARL_U(m_Info1, m_SizeOfEditAndContinueFixedStackFrame, SIZE_OF_EDIT_AND_CONTINUE_FIXED_STACK_FRAME_ENCBASE, EncInfoSize);
#endif
}
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)
{
_ASSERTE( m_SizeOfStackOutgoingAndScratchArea != (UINT32)-1 );
GCINFO_WRITE_VARL_U(m_Info1, NORMALIZE_SIZE_OF_STACK_AREA(m_SizeOfStackOutgoingAndScratchArea), SIZE_OF_STACK_AREA_ENCBASE, FixedAreaSize);
}
#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA
UINT32 numInterruptibleRanges = (UINT32) m_InterruptibleRanges.Count();
InterruptibleRange *pRanges = NULL;
if(numInterruptibleRanges)
{
pRanges = (InterruptibleRange*) m_pAllocator->Alloc(numInterruptibleRanges * sizeof(InterruptibleRange));
m_InterruptibleRanges.CopyTo(pRanges);
}
BitArray liveState(m_pAllocator, m_NumSlots);
BitArray couldBeLive(m_pAllocator, m_NumSlots);
liveState.ClearAll();
couldBeLive.ClearAll();
#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
_ASSERTE(m_NumCallSites == 0 || m_pCallSites != NULL);
///////////////////////////////////////////////////////////////////////
// Normalize call sites
// Eliminate call sites that fall inside interruptible ranges
///////////////////////////////////////////////////////////////////////
UINT32 numCallSites = 0;
for(UINT32 callSiteIndex = 0; callSiteIndex < m_NumCallSites; callSiteIndex++)
{
UINT32 callSite = m_pCallSites[callSiteIndex];
// There's a contract with the EE that says for non-leaf stack frames, where the
// method is stopped at a call site, the EE will not query with the return PC, but
// rather the return PC *minus 1*.
// The reason is that variable/register liveness may change at the instruction immediately after the
// call, so we want such frames to appear as if they are "within" the call.
// Since we use "callSite" as the "key" when we search for the matching descriptor, also subtract 1 here
// (after, of course, adding the size of the call instruction to get the return PC).
callSite += m_pCallSiteSizes[callSiteIndex] - 1;
_ASSERTE(DENORMALIZE_CODE_OFFSET(NORMALIZE_CODE_OFFSET(callSite)) == callSite);
UINT32 normOffset = NORMALIZE_CODE_OFFSET(callSite);
BOOL keepIt = TRUE;
for(UINT32 intRangeIndex = 0; intRangeIndex < numInterruptibleRanges; intRangeIndex++)
{
InterruptibleRange *pRange = &pRanges[intRangeIndex];
if(pRange->NormStopOffset > normOffset)
{
if(pRange->NormStartOffset <= normOffset)
{
keepIt = FALSE;
}
break;
}
}
if(keepIt)
m_pCallSites[numCallSites++] = normOffset;
}
GCINFO_WRITE_VARL_U(m_Info1, NORMALIZE_NUM_SAFE_POINTS(numCallSites), NUM_SAFE_POINTS_ENCBASE, NumCallSitesSize);
m_NumCallSites = numCallSites;
#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
if (slimHeader)
{
_ASSERTE(numInterruptibleRanges == 0);
}
else
{
GCINFO_WRITE_VARL_U(m_Info1, NORMALIZE_NUM_INTERRUPTIBLE_RANGES(numInterruptibleRanges), NUM_INTERRUPTIBLE_RANGES_ENCBASE, NumRangesSize);
}
#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
///////////////////////////////////////////////////////////////////////
// Encode call site offsets
///////////////////////////////////////////////////////////////////////
UINT32 numBitsPerOffset = CeilOfLog2(NORMALIZE_CODE_OFFSET(m_CodeLength));
for(UINT32 callSiteIndex = 0; callSiteIndex < m_NumCallSites; callSiteIndex++)
{
UINT32 normOffset = m_pCallSites[callSiteIndex];
_ASSERTE(normOffset < (UINT32)1 << (numBitsPerOffset+1));
GCINFO_WRITE(m_Info1, normOffset, numBitsPerOffset, CallSitePosSize);
}
#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
///////////////////////////////////////////////////////////////////////
// Encode fully-interruptible ranges
///////////////////////////////////////////////////////////////////////
if(numInterruptibleRanges)
{
UINT32 lastStopOffset = 0;
for(UINT32 i = 0; i < numInterruptibleRanges; i++)
{
UINT32 normStartOffset = pRanges[i].NormStartOffset;
UINT32 normStopOffset = pRanges[i].NormStopOffset;
size_t normStartDelta = normStartOffset - lastStopOffset;
size_t normStopDelta = normStopOffset - normStartOffset;
_ASSERTE(normStopDelta > 0);
lastStopOffset = normStopOffset;
GCINFO_WRITE_VARL_U(m_Info1, normStartDelta, INTERRUPTIBLE_RANGE_DELTA1_ENCBASE, RangeSize);
GCINFO_WRITE_VARL_U(m_Info1, normStopDelta-1, INTERRUPTIBLE_RANGE_DELTA2_ENCBASE, RangeSize);
}
}
///////////////////////////////////////////////////////////////////////
// Pre-process transitions
///////////////////////////////////////////////////////////////////////
size_t numTransitions = m_LifetimeTransitions.Count();
LifetimeTransition *pTransitions = (LifetimeTransition*)m_pAllocator->Alloc(numTransitions * sizeof(LifetimeTransition));
m_LifetimeTransitions.CopyTo(pTransitions);
LifetimeTransition* pEndTransitions = pTransitions + numTransitions;
LifetimeTransition* pCurrent;
//-----------------------------------------------------------------
// Sort the lifetime transitions by offset (then by slot id).
//-----------------------------------------------------------------
// Don't use the CQuickSort algorithm, it's prone to stack overflows
jitstd::sort(
pTransitions,
pTransitions + numTransitions,
CompareLifetimeTransitionsByOffsetThenSlot()
);
// Eliminate transitions outside the method
while(pEndTransitions > pTransitions)
{
LifetimeTransition *pPrev = pEndTransitions - 1;
if(pPrev->CodeOffset < m_CodeLength)
break;
_ASSERTE(pPrev->CodeOffset == m_CodeLength && !pPrev->BecomesLive);
pEndTransitions = pPrev;
}
// Now eliminate any pairs of dead/live transitions for the same slot at the same offset.
EliminateRedundantLiveDeadPairs(&pTransitions, &numTransitions, &pEndTransitions);
#ifdef _DEBUG
numTransitions = -1;
#endif
///////////////////////////////////////////////////////////////////////
// Sort the slot table
///////////////////////////////////////////////////////////////////////
{
GcSlotDescAndId* sortedSlots = (GcSlotDescAndId*) m_pAllocator->Alloc(m_NumSlots * sizeof(GcSlotDescAndId));
UINT32* sortOrder = (UINT32*) m_pAllocator->Alloc(m_NumSlots * sizeof(UINT32));
for(UINT32 i = 0; i < m_NumSlots; i++)
{
sortedSlots[i].m_SlotDesc = m_SlotTable[i];
sortedSlots[i].m_SlotId = i;
}
jitstd::sort(sortedSlots, sortedSlots + m_NumSlots, CompareSlotDescAndIdBySlotDesc());
for(UINT32 i = 0; i < m_NumSlots; i++)
{
sortOrder[sortedSlots[i].m_SlotId] = i;
}
// Re-order the slot table
for(UINT32 i = 0; i < m_NumSlots; i++)
{
m_SlotTable[i] = sortedSlots[i].m_SlotDesc;
}
// Update transitions to assign new slot ids
for(pCurrent = pTransitions; pCurrent < pEndTransitions; pCurrent++)
{
UINT32 newSlotId = sortOrder[pCurrent->SlotId];
pCurrent->SlotId = newSlotId;
}
#ifdef MUST_CALL_JITALLOCATOR_FREE
m_pAllocator->Free( sortedSlots );
m_pAllocator->Free( sortOrder );
#endif
}
#if CODE_OFFSETS_NEED_NORMALIZATION
// Do a pass to normalize transition offsets
for(pCurrent = pTransitions; pCurrent < pEndTransitions; pCurrent++)
{
_ASSERTE(pCurrent->CodeOffset <= m_CodeLength);
pCurrent->CodeOffset = NORMALIZE_CODE_OFFSET(pCurrent->CodeOffset);
}
#endif
///////////////////////////////////////////////////////////////////
// Find out which slots are really used
///////////////////////////////////////////////////////////////////
couldBeLive.ClearAll();
#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
if(m_NumCallSites)
{
_ASSERTE(m_pCallSites != NULL);
liveState.ClearAll();
UINT32 callSiteIndex = 0;
UINT32 callSite = m_pCallSites[0];
for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
{
if(pCurrent->CodeOffset > callSite)
{
couldBeLive |= liveState;
if(++callSiteIndex == m_NumCallSites)
break;
callSite = m_pCallSites[callSiteIndex];
}
else
{
UINT32 slotIndex = pCurrent->SlotId;
if(!DoNotTrackInPartiallyInterruptible(m_SlotTable[slotIndex]))
{
BYTE becomesLive = pCurrent->BecomesLive;
_ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
|| (!liveState.ReadBit(slotIndex) && becomesLive));
liveState.WriteBit(slotIndex, becomesLive);
}
pCurrent++;
}
}
// There could be call sites after the last transition
if(callSiteIndex < m_NumCallSites)
{
couldBeLive |= liveState;
}
}
if(numInterruptibleRanges)
{
liveState.ClearAll();
InterruptibleRange *pCurrentRange = pRanges;
InterruptibleRange *pEndRanges = pRanges + numInterruptibleRanges;
for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
{
// Find the first transition at offset > of the start of the current range
LifetimeTransition *pFirstAfterStart = pCurrent;
while(pFirstAfterStart->CodeOffset <= pCurrentRange->NormStartOffset)
{
UINT32 slotIndex = (UINT32) (pFirstAfterStart->SlotId);
BYTE becomesLive = pFirstAfterStart->BecomesLive;
_ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
|| (!liveState.ReadBit(slotIndex) && becomesLive));
liveState.WriteBit(slotIndex, becomesLive);
if(++pFirstAfterStart == pEndTransitions)
break;
}
couldBeLive |= liveState;
// Now iterate through all the remaining transitions in the range,
// making the offset range-relative, and tracking live state
UINT32 rangeStop = pCurrentRange->NormStopOffset;
for(pCurrent = pFirstAfterStart; pCurrent < pEndTransitions && pCurrent->CodeOffset < rangeStop; pCurrent++)
{
UINT32 slotIndex = (UINT32) (pCurrent->SlotId);
BYTE becomesLive = pCurrent->BecomesLive;
_ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
|| (!liveState.ReadBit(slotIndex) && becomesLive));
liveState.WriteBit(slotIndex, becomesLive);
couldBeLive.SetBit(slotIndex);
}
// Move to the next range
if(pCurrentRange < pEndRanges - 1)
{
pCurrentRange++;
}
else
{
break;
}
}
}
//-----------------------------------------------------------------
// Mark unneeded slots as deleted
//-----------------------------------------------------------------
UINT32 numUsedSlots = 0;
for(UINT32 i = 0; i < m_NumSlots; i++)
{
if(!(m_SlotTable[i].IsUntracked()) && (couldBeLive.ReadBit(i) == 0))
{
m_SlotTable[i].MarkDeleted();
}
else
numUsedSlots++;
}
if(numUsedSlots < m_NumSlots)
{
// Delete transitions on unused slots
LifetimeTransition *pNextFree = pTransitions;
for(pCurrent = pTransitions; pCurrent < pEndTransitions; pCurrent++)
{
UINT32 slotId = pCurrent->SlotId;
if(!m_SlotTable[slotId].IsDeleted())
{
if(pCurrent > pNextFree)
{
*pNextFree = *pCurrent;
}
pNextFree++;
}
}
pEndTransitions = pNextFree;
}
#else // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
UINT32 numUsedSlots = m_NumSlots;
#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
///////////////////////////////////////////////////////////////////////
// Encode slot table
///////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------
// Count registers and stack slots
//------------------------------------------------------------------
UINT32 numRegisters;
UINT32 numUntrackedSlots;
UINT32 numStackSlots;
{
UINT32 numDeleted = 0;
UINT32 i;
for(i = 0; i < m_NumSlots && m_SlotTable[i].IsRegister(); i++)
{
if(m_SlotTable[i].IsDeleted())
{
numDeleted++;
}
}
numRegisters = i - numDeleted;
for(; i < m_NumSlots && !m_SlotTable[i].IsUntracked(); i++)
{
if(m_SlotTable[i].IsDeleted())
{
numDeleted++;
}
}
numStackSlots = i - (numRegisters + numDeleted);
}
numUntrackedSlots = numUsedSlots - (numRegisters + numStackSlots);
// Common case: nothing, or a few registers
if (numRegisters)
{
GCINFO_WRITE(m_Info1, 1, 1, FlagsSize);
GCINFO_WRITE_VARL_U(m_Info1, numRegisters, NUM_REGISTERS_ENCBASE, NumRegsSize);
}
else
{
GCINFO_WRITE(m_Info1, 0, 1, FlagsSize);
}
if (numStackSlots || numUntrackedSlots)
{
GCINFO_WRITE(m_Info1, 1, 1, FlagsSize);
GCINFO_WRITE_VARL_U(m_Info1, numStackSlots, NUM_STACK_SLOTS_ENCBASE, NumStackSize);
GCINFO_WRITE_VARL_U(m_Info1, numUntrackedSlots, NUM_UNTRACKED_SLOTS_ENCBASE, NumUntrackedSize);
}
else
{
GCINFO_WRITE(m_Info1, 0, 1, FlagsSize);
}
UINT32 currentSlot = 0;
if(numUsedSlots == 0)
goto lExitSuccess;
if(numRegisters > 0)
{
GcSlotDesc *pSlotDesc;
do
{
_ASSERTE(currentSlot < m_NumSlots);
pSlotDesc = &m_SlotTable[currentSlot++];
}
while(pSlotDesc->IsDeleted());
_ASSERTE(pSlotDesc->IsRegister());
// Encode slot identification
UINT32 currentNormRegNum = NORMALIZE_REGISTER(pSlotDesc->Slot.RegisterNumber);
GCINFO_WRITE_VARL_U(m_Info1, currentNormRegNum, REGISTER_ENCBASE, RegSlotSize);
GCINFO_WRITE(m_Info1, pSlotDesc->Flags, 2, RegSlotSize);
for(UINT32 j = 1; j < numRegisters; j++)
{
UINT32 lastNormRegNum = currentNormRegNum;
GcSlotFlags lastFlags = pSlotDesc->Flags;
do
{
_ASSERTE(currentSlot < m_NumSlots);
pSlotDesc = &m_SlotTable[currentSlot++];
}
while(pSlotDesc->IsDeleted());
_ASSERTE(pSlotDesc->IsRegister());
currentNormRegNum = NORMALIZE_REGISTER(pSlotDesc->Slot.RegisterNumber);
if(lastFlags != GC_SLOT_IS_REGISTER)
{
GCINFO_WRITE_VARL_U(m_Info1, currentNormRegNum, REGISTER_ENCBASE, RegSlotSize);
GCINFO_WRITE(m_Info1, pSlotDesc->Flags, 2, RegSlotSize);
}
else
{
_ASSERTE(pSlotDesc->Flags == GC_SLOT_IS_REGISTER);
GCINFO_WRITE_VARL_U(m_Info1, currentNormRegNum - lastNormRegNum - 1, REGISTER_DELTA_ENCBASE, RegSlotSize);
}
}
}
if(numStackSlots > 0)
{
GcSlotDesc *pSlotDesc;
do
{
_ASSERTE(currentSlot < m_NumSlots);
pSlotDesc = &m_SlotTable[currentSlot++];
}
while(pSlotDesc->IsDeleted());
_ASSERTE(!pSlotDesc->IsRegister());
_ASSERTE(!pSlotDesc->IsUntracked());
// Encode slot identification
_ASSERTE((pSlotDesc->Slot.Stack.Base & ~3) == 0);
GCINFO_WRITE(m_Info1, pSlotDesc->Slot.Stack.Base, 2, StackSlotSize);
INT32 currentNormStackSlot = NORMALIZE_STACK_SLOT(pSlotDesc->Slot.Stack.SpOffset);
GCINFO_WRITE_VARL_S(m_Info1, currentNormStackSlot, STACK_SLOT_ENCBASE, StackSlotSize);
GCINFO_WRITE(m_Info1, pSlotDesc->Flags, 2, StackSlotSize);
for(UINT32 j = 1; j < numStackSlots; j++)
{
INT32 lastNormStackSlot = currentNormStackSlot;
GcSlotFlags lastFlags = pSlotDesc->Flags;
do
{
_ASSERTE(currentSlot < m_NumSlots);
pSlotDesc = &m_SlotTable[currentSlot++];
}
while(pSlotDesc->IsDeleted());
_ASSERTE(!pSlotDesc->IsRegister());
_ASSERTE(!pSlotDesc->IsUntracked());
currentNormStackSlot = NORMALIZE_STACK_SLOT(pSlotDesc->Slot.Stack.SpOffset);
_ASSERTE((pSlotDesc->Slot.Stack.Base & ~3) == 0);
GCINFO_WRITE(m_Info1, pSlotDesc->Slot.Stack.Base, 2, StackSlotSize);
if(lastFlags != GC_SLOT_BASE)
{
GCINFO_WRITE_VARL_S(m_Info1, currentNormStackSlot, STACK_SLOT_ENCBASE, StackSlotSize);
GCINFO_WRITE(m_Info1, pSlotDesc->Flags, 2, StackSlotSize);
}
else
{
_ASSERTE(pSlotDesc->Flags == GC_SLOT_BASE);
GCINFO_WRITE_VARL_U(m_Info1, currentNormStackSlot - lastNormStackSlot, STACK_SLOT_DELTA_ENCBASE, StackSlotSize);
}
}
}
if(numUntrackedSlots > 0)
{
GcSlotDesc *pSlotDesc;
do
{
_ASSERTE(currentSlot < m_NumSlots);
pSlotDesc = &m_SlotTable[currentSlot++];
}
while(pSlotDesc->IsDeleted());
_ASSERTE(!pSlotDesc->IsRegister());
_ASSERTE(pSlotDesc->IsUntracked());
// Encode slot identification
_ASSERTE((pSlotDesc->Slot.Stack.Base & ~3) == 0);
GCINFO_WRITE(m_Info1, pSlotDesc->Slot.Stack.Base, 2, UntrackedSlotSize);
INT32 currentNormStackSlot = NORMALIZE_STACK_SLOT(pSlotDesc->Slot.Stack.SpOffset);
GCINFO_WRITE_VARL_S(m_Info1, currentNormStackSlot, STACK_SLOT_ENCBASE, UntrackedSlotSize);
GCINFO_WRITE(m_Info1, pSlotDesc->Flags, 2, UntrackedSlotSize);
for(UINT32 j = 1; j < numUntrackedSlots; j++)
{
INT32 lastNormStackSlot = currentNormStackSlot;
GcSlotFlags lastFlags = pSlotDesc->Flags;
do
{
_ASSERTE(currentSlot < m_NumSlots);
pSlotDesc = &m_SlotTable[currentSlot++];
}
while(pSlotDesc->IsDeleted());
_ASSERTE(!pSlotDesc->IsRegister());
_ASSERTE(pSlotDesc->IsUntracked());
currentNormStackSlot = NORMALIZE_STACK_SLOT(pSlotDesc->Slot.Stack.SpOffset);
_ASSERTE((pSlotDesc->Slot.Stack.Base & ~3) == 0);
GCINFO_WRITE(m_Info1, pSlotDesc->Slot.Stack.Base, 2, UntrackedSlotSize);
if(lastFlags != GC_SLOT_UNTRACKED)
{
GCINFO_WRITE_VARL_S(m_Info1, currentNormStackSlot, STACK_SLOT_ENCBASE, UntrackedSlotSize);
GCINFO_WRITE(m_Info1, pSlotDesc->Flags, 2, UntrackedSlotSize);
}
else
{
_ASSERTE(pSlotDesc->Flags == GC_SLOT_UNTRACKED);
GCINFO_WRITE_VARL_U(m_Info1, currentNormStackSlot - lastNormStackSlot, STACK_SLOT_DELTA_ENCBASE, UntrackedSlotSize);
}
}
}
#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
//-----------------------------------------------------------------
// Encode GC info at call sites
//-----------------------------------------------------------------
if(m_NumCallSites)
{
_ASSERTE(m_pCallSites != NULL);
liveState.ClearAll();
UINT32 callSiteIndex = 0;
UINT32 callSite = m_pCallSites[0];
// Create a hash table for storing the locations of the live sets
LiveStateHashTable hashMap(m_pAllocator);
bool outOfMemory = false;
try
{
for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
{
if(pCurrent->CodeOffset > callSite)
{
// Time to record the call site
// Add it to the table if it doesn't exist
UINT32 liveStateOffset = 0;
if (!hashMap.Lookup(&liveState, &liveStateOffset))
{
BitArray * newLiveState = new (m_pAllocator) BitArray(m_pAllocator, m_NumSlots);
*newLiveState = liveState;
hashMap.Set(newLiveState, (UINT32)(-1));
}
if(++callSiteIndex == m_NumCallSites)
break;
callSite = m_pCallSites[callSiteIndex];
}
else
{
UINT32 slotIndex = pCurrent->SlotId;
BYTE becomesLive = pCurrent->BecomesLive;
_ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
|| (!liveState.ReadBit(slotIndex) && becomesLive));
liveState.WriteBit(slotIndex, becomesLive);
pCurrent++;
}
}
// Check for call sites at offsets past the last transition
if (callSiteIndex < m_NumCallSites)
{
UINT32 liveStateOffset = 0;
if (!hashMap.Lookup(&liveState, &liveStateOffset))
{
BitArray * newLiveState = new (m_pAllocator) BitArray(m_pAllocator, m_NumSlots);
*newLiveState = liveState;
hashMap.Set(newLiveState, (UINT32)(-1));
}
}
}
catch (GcInfoNoMemoryException&)
{
outOfMemory = true;
}
if (outOfMemory)
{
m_pNoMem();
}
// Figure out the largest offset, and total size of the sets
// Be sure to figure out the largest offset in the order that we will be emitting
// them in and not the order of their appearances in the safe point array.
// TODO: we should sort this to improve locality (the more frequent ones at the beginning)
// and to improve the indirection size (if the largest one is last, we *might* be able
// so use 1 less bit for each indirection for the offset encoding).
UINT32 largestSetOffset = 0;
UINT32 sizeofSets = 0;
for (LiveStateHashTable::KeyIterator iter = hashMap.Begin(), end = hashMap.End(); !iter.Equal(end); iter.Next())
{
largestSetOffset = sizeofSets;
sizeofSets += SizeofSlotStateVarLengthVector(*iter.Get(), LIVESTATE_RLE_SKIP_ENCBASE, LIVESTATE_RLE_RUN_ENCBASE);
}
// Now that we know the largest offset, we can figure out how much the indirection
// will cost us and commit
UINT32 numBitsPerPointer = ((largestSetOffset < 2) ? 1 : CeilOfLog2(largestSetOffset + 1));
const size_t sizeofEncodedNumBitsPerPointer = BitStreamWriter::SizeofVarLengthUnsigned(numBitsPerPointer, POINTER_SIZE_ENCBASE);
const size_t sizeofNoIndirection = m_NumCallSites * (numRegisters + numStackSlots);
const size_t sizeofIndirection = sizeofEncodedNumBitsPerPointer // Encode the pointer sizes
+ (m_NumCallSites * numBitsPerPointer) // Encode the pointers
+ 7 // Up to 7 bits of alignment padding
+ sizeofSets; // Encode the actual live sets
liveState.ClearAll();
callSiteIndex = 0;
callSite = m_pCallSites[0];
if (sizeofIndirection < sizeofNoIndirection)
{
// we are using an indirection
GCINFO_WRITE(m_Info1, 1, 1, FlagsSize);
GCINFO_WRITE_VARL_U(m_Info1, numBitsPerPointer - 1, POINTER_SIZE_ENCBASE, CallSiteStateSize);
// Now encode the live sets and record the real offset
for (LiveStateHashTable::KeyIterator iter = hashMap.Begin(), end = hashMap.End(); !iter.Equal(end); iter.Next())
{
_ASSERTE(FitsIn<UINT32>(m_Info2.GetBitCount()));
iter.SetValue((UINT32)m_Info2.GetBitCount());
GCINFO_WRITE_VAR_VECTOR(m_Info2, *iter.Get(), LIVESTATE_RLE_SKIP_ENCBASE, LIVESTATE_RLE_RUN_ENCBASE, CallSiteStateSize);
}
_ASSERTE(sizeofSets == m_Info2.GetBitCount());
for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
{
if(pCurrent->CodeOffset > callSite)
{
// Time to encode the call site
// Find the match and emit it
UINT32 liveStateOffset = 0;
bool found = hashMap.Lookup(&liveState, &liveStateOffset);
_ASSERTE(found);
(void)found;
GCINFO_WRITE(m_Info1, liveStateOffset, numBitsPerPointer, CallSiteStateSize);
if(++callSiteIndex == m_NumCallSites)
break;
callSite = m_pCallSites[callSiteIndex];
}
else
{
UINT32 slotIndex = pCurrent->SlotId;
BYTE becomesLive = pCurrent->BecomesLive;
_ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
|| (!liveState.ReadBit(slotIndex) && becomesLive));
liveState.WriteBit(slotIndex, becomesLive);
pCurrent++;
}
}
// Encode call sites at offsets past the last transition
{
UINT32 liveStateOffset = 0;
bool found = hashMap.Lookup(&liveState, &liveStateOffset);
_ASSERTE(found);
(void)found;
for( ; callSiteIndex < m_NumCallSites; callSiteIndex++)
{
GCINFO_WRITE(m_Info1, liveStateOffset, numBitsPerPointer, CallSiteStateSize);
}
}
}
else
{
// we are not using an indirection
GCINFO_WRITE(m_Info1, 0, 1, FlagsSize);
for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
{
if(pCurrent->CodeOffset > callSite)
{
// Time to encode the call site
GCINFO_WRITE_VECTOR(m_Info1, liveState, CallSiteStateSize);
if(++callSiteIndex == m_NumCallSites)
break;
callSite = m_pCallSites[callSiteIndex];
}
else
{
UINT32 slotIndex = pCurrent->SlotId;
BYTE becomesLive = pCurrent->BecomesLive;
_ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
|| (!liveState.ReadBit(slotIndex) && becomesLive));
liveState.WriteBit(slotIndex, becomesLive);
pCurrent++;
}
}
// Encode call sites at offsets past the last transition
for( ; callSiteIndex < m_NumCallSites; callSiteIndex++)
{
GCINFO_WRITE_VECTOR(m_Info1, liveState, CallSiteStateSize);
}
}
#ifdef MUST_CALL_JITALLOCATOR_FREE
// Cleanup
for (LiveStateHashTable::KeyIterator iter = hashMap.Begin(), end = hashMap.End(); !iter.Equal(end); iter.Next())
{
m_pAllocator->Free((LPVOID)iter.Get());
}
#endif // MUST_CALL_JITALLOCATOR_FREE
}
#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
///////////////////////////////////////////////////////////////////////
// Fully-interruptible: Encode lifetime transitions
///////////////////////////////////////////////////////////////////////
if(numInterruptibleRanges)
{
#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
//-----------------------------------------------------
// Under partially-interruptible, make the transition
// offsets relative to the interruptible regions
//-----------------------------------------------------
// Compute total length of interruptible ranges
UINT32 totalInterruptibleLength = 0;
for(UINT32 i = 0; i < numInterruptibleRanges; i++)
{
InterruptibleRange *pRange = &pRanges[i];
totalInterruptibleLength += pRange->NormStopOffset - pRange->NormStartOffset;
}
_ASSERTE(totalInterruptibleLength <= NORMALIZE_CODE_OFFSET(m_CodeLength));
liveState.ClearAll();
// Re-use couldBeLive
BitArray& liveStateAtPrevRange = couldBeLive;
liveStateAtPrevRange.ClearAll();
InterruptibleRange *pCurrentRange = pRanges;
InterruptibleRange *pEndRanges = pRanges + numInterruptibleRanges;
UINT32 cumInterruptibleLength = 0;
for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
{
_ASSERTE(!m_SlotTable[pCurrent->SlotId].IsDeleted());
// Find the first transition at offset > of the start of the current range
LifetimeTransition *pFirstAfterStart = pCurrent;
while(pFirstAfterStart->CodeOffset <= pCurrentRange->NormStartOffset)
{
UINT32 slotIndex = (UINT32) (pFirstAfterStart->SlotId);
BYTE becomesLive = pFirstAfterStart->BecomesLive;
_ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
|| (!liveState.ReadBit(slotIndex) && becomesLive));
liveState.WriteBit(slotIndex, becomesLive);
if(++pFirstAfterStart == pEndTransitions)
break;
}
// Now compare the liveState with liveStateAtPrevRange
LifetimeTransition *pFirstPreserved = pFirstAfterStart;
for(UINT32 slotIndex = 0; slotIndex < m_NumSlots; slotIndex++)
{
uint32_t isLive = liveState.ReadBit(slotIndex);
if(isLive != liveStateAtPrevRange.ReadBit(slotIndex))
{
pFirstPreserved--;
_ASSERTE(pFirstPreserved >= pCurrent);
pFirstPreserved->CodeOffset = cumInterruptibleLength;
pFirstPreserved->SlotId = slotIndex;
pFirstPreserved->BecomesLive = (isLive) ? 1 : 0;
_ASSERTE(!pFirstPreserved->IsDeleted);
}
}
// Mark all the other transitions since last range as deleted
_ASSERTE(pCurrent <= pFirstPreserved);
while(pCurrent < pFirstPreserved)
{
(pCurrent++)->IsDeleted = TRUE;
}
// Now iterate through all the remaining transitions in the range,
// making the offset range-relative, and tracking live state
UINT32 rangeStop = pCurrentRange->NormStopOffset;
for(pCurrent = pFirstAfterStart; pCurrent < pEndTransitions && pCurrent->CodeOffset < rangeStop; pCurrent++)
{
pCurrent->CodeOffset =
pCurrent->CodeOffset -
pCurrentRange->NormStartOffset +
cumInterruptibleLength;
UINT32 slotIndex = (UINT32) (pCurrent->SlotId);
BYTE becomesLive = pCurrent->BecomesLive;
_ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
|| (!liveState.ReadBit(slotIndex) && becomesLive));
liveState.WriteBit(slotIndex, becomesLive);
}
// Move to the next range
if(pCurrentRange < pEndRanges - 1)
{
cumInterruptibleLength += pCurrentRange->NormStopOffset - pCurrentRange->NormStartOffset;
pCurrentRange++;
liveStateAtPrevRange = liveState;
}
else
{
pEndTransitions = pCurrent;
break;
}
}
// Make another pass, deleting everything that's marked as deleted
LifetimeTransition *pNextFree = pTransitions;
for(pCurrent = pTransitions; pCurrent < pEndTransitions; pCurrent++)
{
if(!pCurrent->IsDeleted)
{
if(pCurrent > pNextFree)
{
*pNextFree = *pCurrent;
}
pNextFree++;
}
}
pEndTransitions = pNextFree;
#else
UINT32 totalInterruptibleLength = NORMALIZE_CODE_OFFSET(m_CodeLength);
#endif //PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
//
// Initialize chunk pointers
//
UINT32 numChunks = (totalInterruptibleLength + NUM_NORM_CODE_OFFSETS_PER_CHUNK - 1) / NUM_NORM_CODE_OFFSETS_PER_CHUNK;
_ASSERTE(numChunks > 0);
size_t* pChunkPointers = (size_t*) m_pAllocator->Alloc(numChunks*sizeof(size_t));
ZeroMemory(pChunkPointers, numChunks*sizeof(size_t));
//------------------------------------------------------------------
// Encode transitions
//------------------------------------------------------------------
LOG((LF_GCINFO, LL_INFO1000, "Encoding %i lifetime transitions.\n", pEndTransitions - pTransitions));
liveState.ClearAll();
couldBeLive.ClearAll();
for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
{
_ASSERTE(pCurrent->CodeOffset < m_CodeLength);
UINT32 currentChunk = GetNormCodeOffsetChunk(pCurrent->CodeOffset);
_ASSERTE(currentChunk < numChunks);
UINT32 numTransitionsInCurrentChunk = 1;
for(;;)
{
UINT32 slotIndex = (UINT32) (pCurrent->SlotId);
BYTE becomesLive = pCurrent->BecomesLive;
_ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
|| (!liveState.ReadBit(slotIndex) && becomesLive));
liveState.WriteBit(slotIndex, becomesLive);
couldBeLive.SetBit(slotIndex);
pCurrent++;
if(pCurrent == pEndTransitions || GetNormCodeOffsetChunk(pCurrent->CodeOffset) != currentChunk)
break;
numTransitionsInCurrentChunk++;
}
//-----------------------------------------------------
// Time to encode the chunk
//-----------------------------------------------------
_ASSERTE(numTransitionsInCurrentChunk > 0);
// Sort the transitions in this chunk by slot
jitstd::sort(
pCurrent - numTransitionsInCurrentChunk,
pCurrent,
CompareLifetimeTransitionsBySlot()
);
// Save chunk pointer
pChunkPointers[currentChunk] = m_Info2.GetBitCount() + 1;
// Write couldBeLive slot map
GCINFO_WRITE_VAR_VECTOR(m_Info2, couldBeLive, LIVESTATE_RLE_SKIP_ENCBASE, LIVESTATE_RLE_RUN_ENCBASE, ChunkMaskSize);
LOG((LF_GCINFO, LL_INFO100000,
"Chunk %d couldBeLive (%04x-%04x):\n", currentChunk,
currentChunk * NUM_NORM_CODE_OFFSETS_PER_CHUNK,
((currentChunk + 1) * NUM_NORM_CODE_OFFSETS_PER_CHUNK) - 1
));
// Write final state
// For all the bits set in couldBeLive.
UINT32 i;
for (BitArrayIterator iter(&couldBeLive); !iter.end(); iter++)
{
i = *iter;
{
_ASSERTE(!m_SlotTable[i].IsDeleted());
_ASSERTE(!m_SlotTable[i].IsUntracked());
GCINFO_WRITE( m_Info2,
liveState.ReadBit(i) ? 1 : 0,
1,
ChunkFinalStateSize
);
LOG((LF_GCINFO, LL_INFO100000,
"\t" LOG_GCSLOTDESC_FMT " %s at end of chunk.\n",
LOG_GCSLOTDESC_ARGS(&m_SlotTable[i]),
liveState.ReadBit(i) ? "live" : "dead"));
}
}
// Write transitions offsets
UINT32 normChunkBaseCodeOffset = currentChunk * NUM_NORM_CODE_OFFSETS_PER_CHUNK;
LifetimeTransition* pT = pCurrent - numTransitionsInCurrentChunk;
for (BitArrayIterator iter(&couldBeLive); !iter.end(); iter++)
{
i = *iter;
while(pT < pCurrent)
{
GcSlotId slotId = pT->SlotId;
if(slotId != i)
break;
_ASSERTE(couldBeLive.ReadBit(slotId));
LOG((LF_GCINFO, LL_INFO100000,
"\tTransition " LOG_GCSLOTDESC_FMT " going %s at offset %04x.\n",
LOG_GCSLOTDESC_ARGS(&m_SlotTable[pT->SlotId]),
pT->BecomesLive ? "live" : "dead",
(int) pT->CodeOffset ));
// Write code offset delta
UINT32 normCodeOffset = pT->CodeOffset;
UINT32 normCodeOffsetDelta = normCodeOffset - normChunkBaseCodeOffset;
// Don't encode transitions at offset 0 as they are useless
if(normCodeOffsetDelta)
{
_ASSERTE(normCodeOffsetDelta < NUM_NORM_CODE_OFFSETS_PER_CHUNK);
GCINFO_WRITE(m_Info2, 1, 1, ChunkTransitionSize);
GCINFO_WRITE(m_Info2, normCodeOffsetDelta, NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2, ChunkTransitionSize);
#ifdef MEASURE_GCINFO
m_CurrentMethodSize.NumTransitions++;
#endif
}
pT++;
}
// Write terminator
GCINFO_WRITE(m_Info2, 0, 1, ChunkTransitionSize);
}
_ASSERTE(pT == pCurrent);
couldBeLive = liveState;
}
//---------------------------------------------------------------------
// The size of chunk encodings is now known. Write the chunk pointers.
//---------------------------------------------------------------------
// Find the largest pointer
size_t largestPointer = 0;
for(int i = numChunks - 1; i >=0; i--)
{
largestPointer = pChunkPointers[i];
if(largestPointer > 0)
break;
}
UINT32 numBitsPerPointer = CeilOfLog2(largestPointer + 1);
GCINFO_WRITE_VARL_U(m_Info1, numBitsPerPointer, POINTER_SIZE_ENCBASE, ChunkPtrSize);
if(numBitsPerPointer)
{
for(UINT32 i = 0; i < numChunks; i++)
{
GCINFO_WRITE(m_Info1, pChunkPointers[i], numBitsPerPointer, ChunkPtrSize);
}
}
//-------------------------------------------------------------------
// Cleanup
//-------------------------------------------------------------------
#ifdef MUST_CALL_JITALLOCATOR_FREE
m_pAllocator->Free(pRanges);
m_pAllocator->Free(pChunkPointers);
#endif
}
#ifdef MUST_CALL_JITALLOCATOR_FREE
m_pAllocator->Free(pTransitions);
#endif
lExitSuccess:;
//-------------------------------------------------------------------
// Update global stats
//-------------------------------------------------------------------
#ifdef MEASURE_GCINFO
if (slimHeader)
{
g_NumSlimHeaders++;
}
else
{
g_NumFatHeaders++;
}
m_CurrentMethodSize.NumMethods = 1;
#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
m_CurrentMethodSize.NumCallSites = m_NumCallSites;
#endif
m_CurrentMethodSize.NumRanges = numInterruptibleRanges;
m_CurrentMethodSize.NumRegs = numRegisters;
m_CurrentMethodSize.NumStack = numStackSlots;
m_CurrentMethodSize.NumUntracked = numUntrackedSlots;
m_CurrentMethodSize.SizeOfCode = m_CodeLength;
if(numInterruptibleRanges)
{
g_FiGcInfoSize += m_CurrentMethodSize;
m_CurrentMethodSize.Log(LL_INFO100, "=== FullyInterruptible method breakdown ===\r\n");
g_FiGcInfoSize.Log(LL_INFO10, "=== FullyInterruptible global breakdown ===\r\n");
}
else
{
g_PiGcInfoSize += m_CurrentMethodSize;
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: %zu\n", g_NumSlimHeaders);
LogSpew(LF_GCINFO, LL_INFO10, "NumMethods: %zu\n", g_NumFatHeaders);
#endif
}