summaryrefslogtreecommitdiff
path: root/src/gcinfo/gcinfoencoder.cpp
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
commit4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch)
tree98110734c91668dfdbb126fcc0e15ddbd93738ca /src/gcinfo/gcinfoencoder.cpp
parentfa45f57ed55137c75ac870356a1b8f76c84b229c (diff)
downloadcoreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/gcinfo/gcinfoencoder.cpp')
-rw-r--r--src/gcinfo/gcinfoencoder.cpp2826
1 files changed, 2826 insertions, 0 deletions
diff --git a/src/gcinfo/gcinfoencoder.cpp b/src/gcinfo/gcinfoencoder.cpp
new file mode 100644
index 0000000000..67852220ed
--- /dev/null
+++ b/src/gcinfo/gcinfoencoder.cpp
@@ -0,0 +1,2826 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*****************************************************************************
+ *
+ * GC Information Encoding API
+ *
+ */
+
+#include <stdint.h>
+
+#include "gcinfoencoder.h"
+
+#ifdef _DEBUG
+ #ifndef LOGGING
+ #define LOGGING
+ #endif
+#endif
+
+#ifndef STANDALONE_BUILD
+#include "log.h"
+#include "simplerhash.h"
+#include "bitposition.h"
+#endif
+
+#ifdef MEASURE_GCINFO
+#define GCINFO_WRITE(writer, val, numBits, counter) \
+ { \
+ writer.Write(val, numBits); \
+ m_CurrentMethodSize.counter += numBits; \
+ m_CurrentMethodSize.TotalSize += numBits; \
+ }
+#define GCINFO_WRITE_VARL_U(writer, val, base, counter) \
+ { \
+ size_t __temp = \
+ writer.EncodeVarLengthUnsigned(val, base); \
+ m_CurrentMethodSize.counter += __temp; \
+ m_CurrentMethodSize.TotalSize += __temp; \
+ }
+#define GCINFO_WRITE_VARL_S(writer, val, base, counter) \
+ { \
+ size_t __temp = \
+ writer.EncodeVarLengthSigned(val, base); \
+ m_CurrentMethodSize.counter += __temp; \
+ m_CurrentMethodSize.TotalSize += __temp; \
+ }
+#define GCINFO_WRITE_VECTOR(writer, vector, counter) \
+ { \
+ WriteSlotStateVector(writer, vector); \
+ for(UINT32 i = 0; i < m_NumSlots; i++) \
+ { \
+ if(!m_SlotTable[i].IsDeleted() && \
+ !m_SlotTable[i].IsUntracked()) \
+ { \
+ m_CurrentMethodSize.counter++; \
+ m_CurrentMethodSize.TotalSize++; \
+ } \
+ } \
+ }
+#define GCINFO_WRITE_VAR_VECTOR(writer, vector, baseSkip, baseRun, counter) \
+ { \
+ size_t __temp = \
+ WriteSlotStateVarLengthVector(writer, vector, baseSkip, baseRun); \
+ m_CurrentMethodSize.counter += __temp; \
+ m_CurrentMethodSize.TotalSize += __temp; \
+ }
+#else
+#define GCINFO_WRITE(writer, val, numBits, counter) \
+ writer.Write(val, numBits);
+
+#define GCINFO_WRITE_VARL_U(writer, val, base, counter) \
+ writer.EncodeVarLengthUnsigned(val, base);
+
+#define GCINFO_WRITE_VARL_S(writer, val, base, counter) \
+ writer.EncodeVarLengthSigned(val, base);
+
+#define GCINFO_WRITE_VECTOR(writer, vector, counter) \
+ WriteSlotStateVector(writer, vector);
+
+#define GCINFO_WRITE_VAR_VECTOR(writer, vector, baseSkip, baseRun, counter) \
+ WriteSlotStateVarLengthVector(writer, vector, baseSkip, baseRun);
+#endif
+
+#define LOG_GCSLOTDESC_FMT "%s%c%d%s%s%s"
+#define LOG_GCSLOTDESC_ARGS(pDesc) (pDesc)->IsRegister() ? "register" \
+ : GcStackSlotBaseNames[(pDesc)->Slot.Stack.Base], \
+ (pDesc)->IsRegister() ? ' ' : (pDesc)->Slot.Stack.SpOffset < 0 ? '-' : '+', \
+ (pDesc)->IsRegister() ? (pDesc)->Slot.RegisterNumber \
+ : ((pDesc)->Slot.Stack.SpOffset), \
+ (pDesc)->IsPinned() ? " pinned" : "", \
+ (pDesc)->IsInterior() ? " interior" : "", \
+ (pDesc)->IsUntracked() ? " untracked" : ""
+
+#define LOG_REGTRANSITION_FMT "register %u%s%s"
+#define LOG_REGTRANSITION_ARGS(RegisterNumber, Flags) \
+ RegisterNumber, \
+ (Flags & GC_SLOT_PINNED) ? " pinned" : "", \
+ (Flags & GC_SLOT_INTERIOR) ? " interior" : ""
+
+#define LOG_STACKTRANSITION_FMT "%s%c%d%s%s%s"
+#define LOG_STACKTRANSITION_ARGS(BaseRegister, StackOffset, Flags) \
+ GcStackSlotBaseNames[BaseRegister], \
+ ((StackOffset) < 0) ? '-' : '+', \
+ ((StackOffset) >= 0) ? (StackOffset) \
+ : -(StackOffset), \
+ (Flags & GC_SLOT_PINNED) ? " pinned" : "", \
+ (Flags & GC_SLOT_INTERIOR) ? " interior" : "", \
+ (Flags & GC_SLOT_UNTRACKED) ? " untracked" : ""
+
+class BitArray
+{
+ friend class BitArrayIterator;
+public:
+ BitArray(IAllocator* pJitAllocator, size_t size_tCount)
+ {
+ m_pData = (size_t*)pJitAllocator->Alloc(size_tCount*sizeof(size_t));
+ m_pEndData = m_pData + size_tCount;
+#ifdef MUST_CALL_IALLOCATOR_FREE
+ m_pJitAllocator = pJitAllocator;
+#endif
+ }
+
+ inline size_t* DataPtr()
+ {
+ return m_pData;
+ }
+
+ inline void SetBit( size_t pos )
+ {
+ size_t element = pos / BITS_PER_SIZE_T;
+ int bpos = (int)(pos % BITS_PER_SIZE_T);
+ m_pData[element] |= ((size_t)1 << bpos);
+ }
+
+ inline void ClearBit( size_t pos )
+ {
+ size_t element = pos / BITS_PER_SIZE_T;
+ int bpos = (int)(pos % BITS_PER_SIZE_T);
+ m_pData[element] &= ~((size_t)1 << bpos);
+ }
+
+ inline void SetAll()
+ {
+ size_t* ptr = m_pData;
+ while(ptr < m_pEndData)
+ *(ptr++) = (size_t)(SSIZE_T)(-1);
+ }
+
+ inline void ClearAll()
+ {
+ size_t* ptr = m_pData;
+ while(ptr < m_pEndData)
+ *(ptr++) = (size_t) 0;
+ }
+
+ inline void WriteBit( size_t pos, BOOL val)
+ {
+ if(val)
+ SetBit(pos);
+ else
+ ClearBit(pos);
+ }
+
+ inline size_t ReadBit( size_t pos ) const
+ {
+ size_t element = pos / BITS_PER_SIZE_T;
+ int bpos = (int)(pos % BITS_PER_SIZE_T);
+ return (m_pData[element] & ((size_t)1 << bpos));
+ }
+
+ inline bool operator==(const BitArray &other) const
+ {
+ _ASSERTE(other.m_pEndData - other.m_pData == m_pEndData - m_pData);
+ size_t* dest = m_pData;
+ size_t* src = other.m_pData;
+ return 0 == memcmp(dest, src, (m_pEndData - m_pData) * sizeof(*m_pData));
+ }
+
+ inline int GetHashCode() const
+ {
+ const int* src = (const int*)m_pData;
+ int result = *src++;
+ while(src < (const int*)m_pEndData)
+ result = _rotr(result, 5) ^ *src++;
+
+ return result;
+ }
+
+ inline BitArray& operator=(const BitArray &other)
+ {
+ _ASSERTE(other.m_pEndData - other.m_pData == m_pEndData - m_pData);
+ size_t* dest = m_pData;
+ size_t* src = other.m_pData;
+ while(dest < m_pEndData)
+ *(dest++) = *(src++);
+
+ return *this;
+ }
+
+ inline BitArray& operator|=(const BitArray &other)
+ {
+ _ASSERTE(other.m_pEndData - other.m_pData == m_pEndData - m_pData);
+ size_t* dest = m_pData;
+ size_t* src = other.m_pData;
+ while(dest < m_pEndData)
+ *(dest++) |= *(src++);
+
+ return *this;
+ }
+
+#ifdef MUST_CALL_IALLOCATOR_FREE
+ ~BitArray()
+ {
+ m_pAllocator->Free( m_pData );
+ }
+#endif
+
+ static void* operator new(size_t size, IAllocator* allocator)
+ {
+ return allocator->Alloc(size);
+ }
+
+private:
+ size_t * m_pData;
+ size_t * m_pEndData;
+#ifdef MUST_CALL_IALLOCATOR_FREE
+ IAllocator* m_pJitAllocator;
+#endif
+};
+
+
+class BitArrayIterator
+{
+public:
+ BitArrayIterator(BitArray* bitArray)
+ {
+ m_pCurData = (unsigned *)bitArray->m_pData;
+ m_pEndData = (unsigned *)bitArray->m_pEndData;
+ m_curBits = *m_pCurData;
+ m_curBit = 0;
+ m_curBase = 0;
+ GetNext();
+ }
+ void operator++(int dummy) //int dummy is c++ for "this is postfix ++"
+ {
+ GetNext();
+ }
+
+ void operator++() // prefix ++
+ {
+ GetNext();
+ }
+ void GetNext()
+ {
+ m_curBits -= m_curBit;
+ while (m_curBits == 0)
+ {
+ m_pCurData++;
+ m_curBase += 32;
+ if (m_pCurData == m_pEndData)
+ break;
+ m_curBits = *m_pCurData;
+ }
+ m_curBit = (unsigned)((int)m_curBits & -(int)m_curBits);
+ }
+ unsigned operator*()
+ {
+ assert(!end() && (m_curBit != 0));
+ unsigned bitPosition = BitPosition(m_curBit);
+ return bitPosition + m_curBase;
+ }
+ bool end()
+ {
+ return (m_pCurData == m_pEndData);
+ }
+private:
+ unsigned* m_pCurData;
+ unsigned* m_pEndData;
+ unsigned m_curBits;
+ unsigned m_curBit;
+ unsigned m_curBase;
+};
+
+class LiveStateFuncs
+{
+public:
+ static int GetHashCode(const BitArray * key)
+ {
+ return key->GetHashCode();
+ }
+
+ static bool Equals(const BitArray * k1, const BitArray * k2)
+ {
+ return *k1 == *k2;
+ }
+};
+
+class GcInfoNoMemoryException
+{
+};
+
+class GcInfoHashBehavior
+{
+public:
+ static const unsigned s_growth_factor_numerator = 3;
+ static const unsigned s_growth_factor_denominator = 2;
+
+ static const unsigned s_density_factor_numerator = 3;
+ static const unsigned s_density_factor_denominator = 4;
+
+ static const unsigned s_minimum_allocation = 7;
+
+ inline static void DECLSPEC_NORETURN NoMemory()
+ {
+ throw GcInfoNoMemoryException();
+ }
+};
+
+typedef SimplerHashTable<const BitArray *, LiveStateFuncs, UINT32, GcInfoHashBehavior> LiveStateHashTable;
+
+#ifdef MEASURE_GCINFO
+// Fi = fully-interruptible; we count any method that has one or more interruptible ranges
+// 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& GcInfoSize::operator+=(const GcInfoSize& other)
+{
+ TotalSize += other.TotalSize;
+
+ NumMethods += other.NumMethods;
+ NumCallSites += other.NumCallSites;
+ NumRanges += other.NumRanges;
+ NumRegs += other.NumRegs;
+ NumStack += other.NumStack;
+ NumEh += other.NumEh;
+ NumTransitions += other.NumTransitions;
+ SizeOfCode += other.SizeOfCode;
+
+ FlagsSize += other.FlagsSize;
+ CodeLengthSize += other.CodeLengthSize;
+ ProEpilogSize += other.ProEpilogSize;
+ SecObjSize += other.SecObjSize;
+ GsCookieSize += other.GsCookieSize;
+ GenericsCtxSize += other.GenericsCtxSize;
+ PspSymSize += other.PspSymSize;
+ StackBaseSize += other.StackBaseSize;
+ ReversePInvokeFrameSize += other.ReversePInvokeFrameSize;
+ FixedAreaSize += other.FixedAreaSize;
+ NumCallSitesSize += other.NumCallSitesSize;
+ NumRangesSize += other.NumRangesSize;
+ CallSitePosSize += other.CallSitePosSize;
+ RangeSize += other.RangeSize;
+ NumRegsSize += other.NumRegsSize;
+ NumStackSize += other.NumStackSize;
+ RegSlotSize += other.RegSlotSize;
+ StackSlotSize += other.StackSlotSize;
+ CallSiteStateSize += other.CallSiteStateSize;
+ NumEhSize += other.NumEhSize;
+ EhPosSize += other.EhPosSize;
+ EhStateSize += other.EhStateSize;
+ ChunkPtrSize += other.ChunkPtrSize;
+ ChunkMaskSize += other.ChunkMaskSize;
+ ChunkFinalStateSize += other.ChunkFinalStateSize;
+ ChunkTransitionSize += other.ChunkTransitionSize;
+
+ return *this;
+}
+
+void GcInfoSize::Log(DWORD level, const char * header)
+{
+ if(LoggingOn(LF_GCINFO, level))
+ {
+ LogSpew(LF_GCINFO, level, header);
+
+ LogSpew(LF_GCINFO, level, "---COUNTS---\n");
+ LogSpew(LF_GCINFO, level, "NumMethods: %Iu\n", NumMethods);
+ LogSpew(LF_GCINFO, level, "NumCallSites: %Iu\n", NumCallSites);
+ LogSpew(LF_GCINFO, level, "NumRanges: %Iu\n", NumRanges);
+ LogSpew(LF_GCINFO, level, "NumRegs: %Iu\n", NumRegs);
+ LogSpew(LF_GCINFO, level, "NumStack: %Iu\n", NumStack);
+ LogSpew(LF_GCINFO, level, "NumEh: %Iu\n", NumEh);
+ LogSpew(LF_GCINFO, level, "NumTransitions: %Iu\n", NumTransitions);
+ LogSpew(LF_GCINFO, level, "SizeOfCode: %Iu\n", SizeOfCode);
+
+ LogSpew(LF_GCINFO, level, "---SIZES(bits)---\n");
+ LogSpew(LF_GCINFO, level, "Total: %Iu\n", TotalSize);
+ LogSpew(LF_GCINFO, level, "Flags: %Iu\n", FlagsSize);
+ LogSpew(LF_GCINFO, level, "CodeLength: %Iu\n", CodeLengthSize);
+ LogSpew(LF_GCINFO, level, "Prolog/Epilog: %Iu\n", ProEpilogSize);
+ LogSpew(LF_GCINFO, level, "SecObj: %Iu\n", SecObjSize);
+ 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, "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);
+ LogSpew(LF_GCINFO, level, "Ranges: %Iu\n", RangeSize);
+ LogSpew(LF_GCINFO, level, "NumRegs: %Iu\n", NumRegsSize);
+ LogSpew(LF_GCINFO, level, "NumStack: %Iu\n", NumStackSize);
+ LogSpew(LF_GCINFO, level, "RegSlots: %Iu\n", RegSlotSize);
+ LogSpew(LF_GCINFO, level, "StackSlots: %Iu\n", StackSlotSize);
+ LogSpew(LF_GCINFO, level, "CallSiteStates: %Iu\n", CallSiteStateSize);
+ LogSpew(LF_GCINFO, level, "NumEh: %Iu\n", NumEhSize);
+ LogSpew(LF_GCINFO, level, "EhOffsets: %Iu\n", EhPosSize);
+ LogSpew(LF_GCINFO, level, "EhStates: %Iu\n", EhStateSize);
+ LogSpew(LF_GCINFO, level, "ChunkPointers: %Iu\n", ChunkPtrSize);
+ LogSpew(LF_GCINFO, level, "ChunkMasks: %Iu\n", ChunkMaskSize);
+ LogSpew(LF_GCINFO, level, "ChunkFinalStates: %Iu\n", ChunkFinalStateSize);
+ LogSpew(LF_GCINFO, level, "Transitions: %Iu\n", ChunkTransitionSize);
+ }
+}
+
+#endif
+
+#ifndef DISABLE_EH_VECTORS
+inline BOOL IsEssential(EE_ILEXCEPTION_CLAUSE *pClause)
+{
+ _ASSERTE(pClause->TryEndPC >= pClause->TryStartPC);
+ if(pClause->TryEndPC == pClause->TryStartPC)
+ return FALSE;
+
+ return TRUE;
+}
+#endif
+
+GcInfoEncoder::GcInfoEncoder(
+ ICorJitInfo* pCorJitInfo,
+ CORINFO_METHOD_INFO* pMethodInfo,
+ IAllocator* pJitAllocator,
+ NoMemoryFunction pNoMem
+ )
+ : m_Info1( pJitAllocator ),
+ m_Info2( pJitAllocator ),
+ m_InterruptibleRanges( pJitAllocator ),
+ m_LifetimeTransitions( pJitAllocator )
+{
+#ifdef MEASURE_GCINFO
+ // This causes multiple complus.log files in JIT64. TODO: consider using ICorJitInfo::logMsg instead.
+ InitializeLogging();
+#endif
+
+ _ASSERTE( pCorJitInfo != NULL );
+ _ASSERTE( pMethodInfo != NULL );
+ _ASSERTE( pJitAllocator != NULL );
+ _ASSERTE( pNoMem != NULL );
+
+ m_pCorJitInfo = pCorJitInfo;
+ m_pMethodInfo = pMethodInfo;
+ m_pAllocator = pJitAllocator;
+ m_pNoMem = pNoMem;
+
+#ifdef _DEBUG
+ CORINFO_METHOD_HANDLE methodHandle = pMethodInfo->ftn;
+
+ // Get the name of the current method along with the enclosing class
+ // or module name.
+ m_MethodName =
+ pCorJitInfo->getMethodName(methodHandle, (const char **)&m_ModuleName);
+#endif
+
+
+ m_SlotTableSize = m_SlotTableInitialSize;
+ m_SlotTable = (GcSlotDesc*) m_pAllocator->Alloc( m_SlotTableSize*sizeof(GcSlotDesc) );
+ m_NumSlots = 0;
+#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
+ m_pCallSites = NULL;
+ m_pCallSiteSizes = NULL;
+ m_NumCallSites = 0;
+#endif
+
+ m_SecurityObjectStackSlot = NO_SECURITY_OBJECT;
+ m_GSCookieStackSlot = NO_GS_COOKIE;
+ m_GSCookieValidRangeStart = 0;
+ _ASSERTE(sizeof(m_GSCookieValidRangeEnd) == sizeof(UINT32));
+ m_GSCookieValidRangeEnd = (UINT32) (-1); // == UINT32.MaxValue
+ m_PSPSymStackSlot = NO_PSP_SYM;
+ m_GenericsInstContextStackSlot = NO_GENERICS_INST_CONTEXT;
+ m_contextParamType = GENERIC_CONTEXTPARAM_NONE;
+
+ 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
+
+}
+
+#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
+void GcInfoEncoder::DefineCallSites(UINT32* pCallSites, BYTE* pCallSiteSizes, UINT32 numCallSites)
+{
+ m_pCallSites = pCallSites;
+ m_pCallSiteSizes = pCallSiteSizes;
+ m_NumCallSites = numCallSites;
+#ifdef _DEBUG
+ for(UINT32 i=0; i<numCallSites; i++)
+ {
+ _ASSERTE(pCallSiteSizes[i] > 0);
+ _ASSERTE(DENORMALIZE_CODE_OFFSET(NORMALIZE_CODE_OFFSET(pCallSites[i])) == pCallSites[i]);
+ if(i > 0)
+ {
+ UINT32 prevEnd = pCallSites[i-1] + pCallSiteSizes[i-1];
+ UINT32 curStart = pCallSites[i];
+ _ASSERTE(curStart >= prevEnd);
+ }
+ }
+#endif
+}
+#endif
+
+GcSlotId GcInfoEncoder::GetRegisterSlotId( UINT32 regNum, GcSlotFlags flags )
+{
+ // We could lookup an existing identical slot in the slot table (via some hashtable mechanism).
+ // We just create duplicates for now.
+
+#ifdef _DEBUG
+ _ASSERTE( !m_IsSlotTableFrozen );
+#endif
+
+ if( m_NumSlots == m_SlotTableSize )
+ {
+ GrowSlotTable();
+ }
+ _ASSERTE( m_NumSlots < m_SlotTableSize );
+
+ _ASSERTE( (flags & (GC_SLOT_IS_REGISTER | GC_SLOT_IS_DELETED | GC_SLOT_UNTRACKED)) == 0 );
+ m_SlotTable[ m_NumSlots ].Slot.RegisterNumber = regNum;
+ m_SlotTable[ m_NumSlots ].Flags = (GcSlotFlags) (flags | GC_SLOT_IS_REGISTER);
+
+ GcSlotId newSlotId;
+ newSlotId = m_NumSlots++;
+
+ return newSlotId;
+}
+
+GcSlotId GcInfoEncoder::GetStackSlotId( INT32 spOffset, GcSlotFlags flags, GcStackSlotBase spBase )
+{
+ // We could lookup an existing identical slot in the slot table (via some hashtable mechanism).
+ // We just create duplicates for now.
+
+#ifdef _DEBUG
+ _ASSERTE( !m_IsSlotTableFrozen );
+#endif
+
+ if( m_NumSlots == m_SlotTableSize )
+ {
+ GrowSlotTable();
+ }
+ _ASSERTE( m_NumSlots < m_SlotTableSize );
+
+ // Not valid to reference anything below the current stack pointer
+ _ASSERTE(GC_SP_REL != spBase || spOffset >= 0);
+
+ _ASSERTE( (flags & (GC_SLOT_IS_REGISTER | GC_SLOT_IS_DELETED)) == 0 );
+
+ // the spOffset for the stack slot is required to be pointer size aligned
+ _ASSERTE((spOffset % TARGET_POINTER_SIZE) == 0);
+
+ m_SlotTable[ m_NumSlots ].Slot.Stack.SpOffset = spOffset;
+ m_SlotTable[ m_NumSlots ].Slot.Stack.Base = spBase;
+ m_SlotTable[ m_NumSlots ].Flags = flags;
+
+ GcSlotId newSlotId;
+ newSlotId = m_NumSlots++;
+
+ return newSlotId;
+}
+
+void GcInfoEncoder::GrowSlotTable()
+{
+ m_SlotTableSize *= 2;
+ GcSlotDesc* newSlotTable = (GcSlotDesc*) m_pAllocator->Alloc( m_SlotTableSize * sizeof(GcSlotDesc) );
+ memcpy( newSlotTable, m_SlotTable, m_NumSlots * sizeof(GcSlotDesc) );
+
+#ifdef MUST_CALL_JITALLOCATOR_FREE
+ m_pAllocator->Free( m_SlotTable );
+#endif
+
+ m_SlotTable = newSlotTable;
+}
+
+void GcInfoEncoder::WriteSlotStateVector(BitStreamWriter &writer, const BitArray& vector)
+{
+ for(UINT32 i = 0; i < m_NumSlots && !m_SlotTable[i].IsUntracked(); i++)
+ {
+ if(!m_SlotTable[i].IsDeleted())
+ writer.Write(vector.ReadBit(i) ? 1 : 0, 1);
+ else
+ _ASSERTE(vector.ReadBit(i) == 0);
+ }
+}
+
+void GcInfoEncoder::DefineInterruptibleRange( UINT32 startInstructionOffset, UINT32 length )
+{
+ UINT32 stopInstructionOffset = startInstructionOffset + length;
+
+ UINT32 normStartOffset = NORMALIZE_CODE_OFFSET(startInstructionOffset);
+ UINT32 normStopOffset = NORMALIZE_CODE_OFFSET(stopInstructionOffset);
+
+ // Ranges must not overlap and must be passed sorted by increasing offset
+ _ASSERTE(
+ m_pLastInterruptibleRange == NULL ||
+ normStartOffset >= m_pLastInterruptibleRange->NormStopOffset
+ );
+
+ // Ignore empty ranges
+ if(normStopOffset > normStartOffset)
+ {
+ if(m_pLastInterruptibleRange
+ && normStartOffset == m_pLastInterruptibleRange->NormStopOffset)
+ {
+ // Merge adjacent ranges
+ m_pLastInterruptibleRange->NormStopOffset = normStopOffset;
+ }
+ else
+ {
+ InterruptibleRange range;
+ range.NormStartOffset = normStartOffset;
+ range.NormStopOffset = normStopOffset;
+ m_pLastInterruptibleRange = m_InterruptibleRanges.Append();
+ *m_pLastInterruptibleRange = range;
+ }
+ }
+
+ LOG((LF_GCINFO, LL_INFO1000000, "interruptible at %x length %x\n", startInstructionOffset, length));
+}
+
+
+
+//
+// For inputs, pass zero as offset
+//
+void GcInfoEncoder::SetSlotState(
+ UINT32 instructionOffset,
+ GcSlotId slotId,
+ GcSlotState slotState
+ )
+{
+ _ASSERTE( (m_SlotTable[ slotId ].Flags & GC_SLOT_UNTRACKED) == 0 );
+
+ LifetimeTransition transition;
+
+ transition.SlotId = slotId;
+ transition.CodeOffset = instructionOffset;
+ transition.BecomesLive = ( slotState == GC_SLOT_LIVE );
+ transition.IsDeleted = FALSE;
+
+ *( m_LifetimeTransitions.Append() ) = transition;
+
+ LOG((LF_GCINFO, LL_INFO1000000, LOG_GCSLOTDESC_FMT " %s at %x\n", LOG_GCSLOTDESC_ARGS(&m_SlotTable[slotId]), slotState == GC_SLOT_LIVE ? "live" : "dead", instructionOffset));
+}
+
+
+void GcInfoEncoder::SetIsVarArg()
+{
+ m_IsVarArg = true;
+}
+
+void GcInfoEncoder::SetCodeLength( UINT32 length )
+{
+ _ASSERTE( length > 0 );
+ _ASSERTE( m_CodeLength == 0 || m_CodeLength == length );
+ m_CodeLength = length;
+}
+
+
+void GcInfoEncoder::SetSecurityObjectStackSlot( INT32 spOffset )
+{
+ _ASSERTE( spOffset != NO_SECURITY_OBJECT );
+#if defined(_TARGET_AMD64_)
+ _ASSERTE( spOffset < 0x10 && "The security object cannot reside in an input variable!" );
+#endif
+ _ASSERTE( m_SecurityObjectStackSlot == NO_SECURITY_OBJECT || m_SecurityObjectStackSlot == spOffset );
+
+ m_SecurityObjectStackSlot = spOffset;
+}
+
+void GcInfoEncoder::SetPrologSize( UINT32 prologSize )
+{
+ _ASSERTE(prologSize != 0);
+ _ASSERTE(m_GSCookieValidRangeStart == 0 || m_GSCookieValidRangeStart == prologSize);
+ _ASSERTE(m_GSCookieValidRangeEnd == (UINT32)(-1) || m_GSCookieValidRangeEnd == prologSize+1);
+
+ m_GSCookieValidRangeStart = prologSize;
+ // satisfy asserts that assume m_GSCookieValidRangeStart != 0 ==> m_GSCookieValidRangeStart < m_GSCookieValidRangeEnd
+ m_GSCookieValidRangeEnd = prologSize+1;
+}
+
+void GcInfoEncoder::SetGSCookieStackSlot( INT32 spOffsetGSCookie, UINT32 validRangeStart, UINT32 validRangeEnd )
+{
+ _ASSERTE( spOffsetGSCookie != NO_GS_COOKIE );
+ _ASSERTE( m_GSCookieStackSlot == NO_GS_COOKIE || m_GSCookieStackSlot == spOffsetGSCookie );
+ _ASSERTE( validRangeStart < validRangeEnd );
+
+ m_GSCookieStackSlot = spOffsetGSCookie;
+ m_GSCookieValidRangeStart = validRangeStart;
+ m_GSCookieValidRangeEnd = validRangeEnd;
+}
+
+void GcInfoEncoder::SetPSPSymStackSlot( INT32 spOffsetPSPSym )
+{
+ _ASSERTE( spOffsetPSPSym != NO_PSP_SYM );
+ _ASSERTE( m_PSPSymStackSlot == NO_PSP_SYM || m_PSPSymStackSlot == spOffsetPSPSym );
+
+ m_PSPSymStackSlot = spOffsetPSPSym;
+}
+
+void GcInfoEncoder::SetGenericsInstContextStackSlot( INT32 spOffsetGenericsContext, GENERIC_CONTEXTPARAM_TYPE type)
+{
+ _ASSERTE( spOffsetGenericsContext != NO_GENERICS_INST_CONTEXT);
+ _ASSERTE( m_GenericsInstContextStackSlot == NO_GENERICS_INST_CONTEXT || m_GenericsInstContextStackSlot == spOffsetGenericsContext );
+
+ m_GenericsInstContextStackSlot = spOffsetGenericsContext;
+ m_contextParamType = type;
+}
+
+void GcInfoEncoder::SetStackBaseRegister( UINT32 regNum )
+{
+ _ASSERTE( regNum != NO_STACK_BASE_REGISTER );
+ _ASSERTE(DENORMALIZE_STACK_BASE_REGISTER(NORMALIZE_STACK_BASE_REGISTER(regNum)) == regNum);
+ _ASSERTE( m_StackBaseRegister == NO_STACK_BASE_REGISTER || m_StackBaseRegister == regNum );
+ m_StackBaseRegister = regNum;
+}
+
+void GcInfoEncoder::SetSizeOfEditAndContinuePreservedArea( UINT32 slots )
+{
+ _ASSERTE( slots != NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA );
+ _ASSERTE( m_SizeOfEditAndContinuePreservedArea == NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA );
+ m_SizeOfEditAndContinuePreservedArea = slots;
+}
+
+void GcInfoEncoder::SetWantsReportOnlyLeaf()
+{
+ m_WantsReportOnlyLeaf = true;
+}
+
+#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
+void GcInfoEncoder::SetSizeOfStackOutgoingAndScratchArea( UINT32 size )
+{
+ _ASSERTE( size != (UINT32)-1 );
+ _ASSERTE( m_SizeOfStackOutgoingAndScratchArea == (UINT32)-1 || m_SizeOfStackOutgoingAndScratchArea == size );
+ m_SizeOfStackOutgoingAndScratchArea = 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
+{
+ GcSlotDesc m_SlotDesc;
+ UINT32 m_SlotId;
+};
+
+int __cdecl CompareSlotDescAndIdBySlotDesc(const void* p1, const void* p2)
+{
+ const GcSlotDesc* pFirst = &reinterpret_cast<const GcSlotDescAndId*>(p1)->m_SlotDesc;
+ const GcSlotDesc* pSecond = &reinterpret_cast<const GcSlotDescAndId*>(p2)->m_SlotDesc;
+
+ int firstFlags = pFirst->Flags ^ GC_SLOT_UNTRACKED;
+ int secondFlags = pSecond->Flags ^ GC_SLOT_UNTRACKED;
+
+ // All registers come before all stack slots
+ // All untracked come last
+ // Then sort them by flags, ensuring that the least-frequent interior/pinned flag combinations are first
+ // This is accomplished in the comparison of flags, since we encode IsRegister in the highest flag bit
+ // And we XOR the UNTRACKED flag to place them last in the second highest flag bit
+ if( firstFlags > secondFlags ) return -1;
+ if( firstFlags < secondFlags ) return 1;
+
+ // Then sort them by slot
+ if( pFirst->IsRegister() )
+ {
+ _ASSERTE( pSecond->IsRegister() );
+ if( pFirst->Slot.RegisterNumber < pSecond->Slot.RegisterNumber ) return -1;
+ if( pFirst->Slot.RegisterNumber > pSecond->Slot.RegisterNumber ) return 1;
+ }
+ else
+ {
+ _ASSERTE( !pSecond->IsRegister() );
+ if( pFirst->Slot.Stack.SpOffset < pSecond->Slot.Stack.SpOffset ) return -1;
+ if( pFirst->Slot.Stack.SpOffset > pSecond->Slot.Stack.SpOffset ) return 1;
+
+ // This is arbitrary, but we want to make sure they are considered separate slots
+ if( pFirst->Slot.Stack.Base < pSecond->Slot.Stack.Base ) return -1;
+ if( pFirst->Slot.Stack.Base > pSecond->Slot.Stack.Base ) return 1;
+ }
+
+ // If we get here, the slots are identical
+ _ASSERTE(!"Duplicate slots definitions found in GC information!");
+ return 0;
+}
+
+
+int __cdecl CompareLifetimeTransitionsByOffsetThenSlot(const void* p1, const void* p2)
+{
+ const GcInfoEncoder::LifetimeTransition* pFirst = (const GcInfoEncoder::LifetimeTransition*) p1;
+ const GcInfoEncoder::LifetimeTransition* pSecond = (const GcInfoEncoder::LifetimeTransition*) p2;
+
+ UINT32 firstOffset = pFirst->CodeOffset;
+ UINT32 secondOffset = pSecond->CodeOffset;
+
+ if (firstOffset == secondOffset)
+ {
+ return pFirst->SlotId - pSecond->SlotId;
+ }
+ else
+ {
+ return firstOffset - secondOffset;
+ }
+}
+
+
+int __cdecl CompareLifetimeTransitionsBySlot(const void* p1, const void* p2)
+{
+ const GcInfoEncoder::LifetimeTransition* pFirst = (const GcInfoEncoder::LifetimeTransition*) p1;
+ const GcInfoEncoder::LifetimeTransition* pSecond = (const GcInfoEncoder::LifetimeTransition*) p2;
+
+ UINT32 firstOffset = pFirst->CodeOffset;
+ UINT32 secondOffset = pSecond->CodeOffset;
+
+ _ASSERTE(GetNormCodeOffsetChunk(firstOffset) == GetNormCodeOffsetChunk(secondOffset));
+
+ // Sort them by slot
+ if( pFirst->SlotId < pSecond->SlotId ) return -1;
+ if( pFirst->SlotId > pSecond->SlotId ) return 1;
+
+ // Then sort them by code offset
+ if( firstOffset < secondOffset )
+ return -1;
+ else
+ {
+ _ASSERTE(( firstOffset > secondOffset ) && "Redundant transitions found in GC info!");
+ return 1;
+ }
+}
+
+BitStreamWriter::MemoryBlockList::MemoryBlockList()
+ : m_head(nullptr),
+ m_tail(nullptr)
+{
+}
+
+BitStreamWriter::MemoryBlock* BitStreamWriter::MemoryBlockList::AppendNew(IAllocator* allocator, size_t bytes)
+{
+ auto* memBlock = reinterpret_cast<MemoryBlock*>(allocator->Alloc(sizeof(MemoryBlock) + bytes));
+ memBlock->m_next = nullptr;
+
+ if (m_tail != nullptr)
+ {
+ _ASSERTE(m_head != nullptr);
+ m_tail->m_next = memBlock;
+ }
+ else
+ {
+ _ASSERTE(m_head == nullptr);
+ m_head = memBlock;
+ }
+
+ m_tail = memBlock;
+ return memBlock;
+}
+
+void BitStreamWriter::MemoryBlockList::Dispose(IAllocator* allocator)
+{
+#ifdef MUST_CALL_JITALLOCATOR_FREE
+ for (MemoryBlock* block = m_head, *next; block != nullptr; block = next)
+ {
+ next = block->m_next;
+ allocator->Free(block);
+ }
+ m_head = nullptr;
+ m_tail = nullptr;
+#endif
+}
+
+void BitStreamWriter::Write(BitArray& a, UINT32 count)
+{
+ size_t* dataPtr = a.DataPtr();
+ for(;;)
+ {
+ if(count <= BITS_PER_SIZE_T)
+ {
+ Write(*dataPtr, count);
+ break;
+ }
+ Write(*(dataPtr++), BITS_PER_SIZE_T);
+ count -= BITS_PER_SIZE_T;
+ }
+}
+
+void GcInfoEncoder::FinalizeSlotIds()
+{
+#ifdef _DEBUG
+ m_IsSlotTableFrozen = TRUE;
+#endif
+}
+
+bool GcInfoEncoder::IsAlwaysScratch(GcSlotDesc &slotDesc)
+{
+#if defined(_TARGET_ARM_)
+
+ _ASSERTE( m_SizeOfStackOutgoingAndScratchArea != (UINT32)-1 );
+ if(slotDesc.IsRegister())
+ {
+ int regNum = (int) slotDesc.Slot.RegisterNumber;
+ _ASSERTE(regNum >= 0 && regNum <= 14);
+ _ASSERTE(regNum != 13); // sp
+
+ return ((regNum <= 3) || (regNum >= 12)); // R12 and R14/LR are both scratch registers
+ }
+ else if (!slotDesc.IsUntracked() && (slotDesc.Slot.Stack.Base == GC_SP_REL) &&
+ ((UINT32)slotDesc.Slot.Stack.SpOffset < m_SizeOfStackOutgoingAndScratchArea))
+ {
+ return TRUE;
+ }
+ else
+ return FALSE;
+
+#elif defined(_TARGET_AMD64_)
+
+ _ASSERTE( m_SizeOfStackOutgoingAndScratchArea != (UINT32)-1 );
+ if(slotDesc.IsRegister())
+ {
+ int regNum = (int) slotDesc.Slot.RegisterNumber;
+ _ASSERTE(regNum >= 0 && regNum <= 16);
+ _ASSERTE(regNum != 4); // rsp
+
+ UINT16 PreservedRegMask =
+ (1 << 3) // rbx
+ | (1 << 5) // rbp
+#ifndef UNIX_AMD64_ABI
+ | (1 << 6) // rsi
+ | (1 << 7) // rdi
+#endif // UNIX_AMD64_ABI
+ | (1 << 12) // r12
+ | (1 << 13) // r13
+ | (1 << 14) // r14
+ | (1 << 15); // r15
+
+ return !(PreservedRegMask & (1 << regNum));
+ }
+ else if (!slotDesc.IsUntracked() && (slotDesc.Slot.Stack.Base == GC_SP_REL) &&
+ ((UINT32)slotDesc.Slot.Stack.SpOffset < m_SizeOfStackOutgoingAndScratchArea))
+ {
+ return TRUE;
+ }
+ else
+ return FALSE;
+
+#else
+ return FALSE;
+#endif
+}
+
+void GcInfoEncoder::Build()
+{
+#ifdef _DEBUG
+ _ASSERTE(m_IsSlotTableFrozen || m_NumSlots == 0);
+#endif
+
+ _ASSERTE((1 << NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2) == NUM_NORM_CODE_OFFSETS_PER_CHUNK);
+
+ LOG((LF_GCINFO, LL_INFO100,
+ "Entering GcInfoEncoder::Build() for method %s[%s]\n",
+ m_MethodName, m_ModuleName
+ ));
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // 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) && !hasReversePInvokeFrame &&
+ ((m_StackBaseRegister == NO_STACK_BASE_REGISTER) || (NORMALIZE_STACK_BASE_REGISTER(m_StackBaseRegister) == 0))) &&
+ (m_SizeOfEditAndContinuePreservedArea == NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA) &&
+ !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
+ 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, (hasSecurityObject ? 1 : 0), 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);
+ 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 );
+ _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 (hasSecurityObject || 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 security object.
+ if(hasSecurityObject)
+ {
+ _ASSERTE(!slimHeader);
+#ifdef _DEBUG
+ LOG((LF_GCINFO, LL_INFO1000, "Security object at " FMT_STK "\n",
+ DBG_STK(m_SecurityObjectStackSlot)
+ ));
+#endif
+
+ GCINFO_WRITE_VARL_S(m_Info1, NORMALIZE_STACK_SLOT(m_SecurityObjectStackSlot), SECURITY_OBJECT_STACK_SLOT_ENCBASE, SecObjSize);
+ }
+
+ // 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))
+ {
+ 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, 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)
+ {
+ _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);
+ }
+
+ int size_tCount = (m_NumSlots + BITS_PER_SIZE_T - 1) / BITS_PER_SIZE_T;
+ BitArray liveState(m_pAllocator, size_tCount);
+ BitArray couldBeLive(m_pAllocator, size_tCount);
+
+
+#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
+ qsort(
+ pTransitions,
+ numTransitions,
+ sizeof(LifetimeTransition),
+ 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;
+ }
+
+ qsort(sortedSlots, m_NumSlots, sizeof(GcSlotDescAndId), 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
+ }
+
+
+#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
+ ///////////////////////////////////////////////////////////////////////
+ // Gather EH information
+ ///////////////////////////////////////////////////////////////////////
+
+ couldBeLive.ClearAll();
+
+#ifndef DISABLE_EH_VECTORS
+ UINT32 numEHClauses;
+ EE_ILEXCEPTION *pEHInfo = (EE_ILEXCEPTION*) m_pCorJitInfo->getEHInfo();
+ if (!pEHInfo)
+ numEHClauses = 0;
+ else
+ numEHClauses = pEHInfo->EHCount();
+
+ UINT32 numUsedEHClauses = numEHClauses;
+ for (UINT32 clauseIndex = 0; clauseIndex < numEHClauses; clauseIndex++)
+ {
+ EE_ILEXCEPTION_CLAUSE * pClause;
+ pClause = pEHInfo->EHClause(clauseIndex);
+
+ if(!IsEssential(pClause))
+ numUsedEHClauses--;
+ }
+
+ UINT32 ehTableBitCount = m_NumSlots * numUsedEHClauses;
+ BitArray ehLiveSlots(m_pAllocator, (ehTableBitCount + BITS_PER_SIZE_T - 1) / BITS_PER_SIZE_T);
+ ehLiveSlots.ClearAll();
+
+ UINT32 basePos = 0;
+ for (UINT32 clauseIndex = 0; clauseIndex < numEHClauses; clauseIndex++)
+ {
+ EE_ILEXCEPTION_CLAUSE * pClause;
+ pClause = pEHInfo->EHClause(clauseIndex);
+
+ _ASSERTE(pClause->TryEndPC <= m_CodeLength);
+ if(!IsEssential(pClause))
+ continue;
+
+ liveState.ClearAll();
+
+ for(pCurrent = pTransitions; pCurrent < pEndTransitions; pCurrent++)
+ {
+ if(pCurrent->CodeOffset > pClause->TryStartPC)
+ break;
+
+ UINT32 slotIndex = pCurrent->SlotId;
+ BYTE becomesLive = pCurrent->BecomesLive;
+ _ASSERTE(liveState.ReadBit(slotIndex) && !becomesLive
+ || !liveState.ReadBit(slotIndex) && becomesLive);
+ liveState.WriteBit(slotIndex, becomesLive);
+ }
+
+ for( ; pCurrent < pEndTransitions; pCurrent++)
+ {
+ if(pCurrent->CodeOffset >= pClause->TryEndPC)
+ break;
+
+ UINT32 slotIndex = pCurrent->SlotId;
+ liveState.ClearBit(slotIndex);
+ }
+
+ // Copy to the EH live state table
+ for(UINT32 i = 0; i < m_NumSlots; i++)
+ {
+ if(liveState.ReadBit(i))
+ ehLiveSlots.SetBit(basePos + i);
+ }
+ basePos += m_NumSlots;
+
+ // Keep track of which slots are used
+ couldBeLive |= liveState;
+ }
+#endif // DISABLE_EH_VECTORS
+#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
+
+#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
+ ///////////////////////////////////////////////////////////////////
+
+
+#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(!IsAlwaysScratch(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, size_tCount);
+ *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, size_tCount);
+ *newLiveState = liveState;
+ hashMap.Set(newLiveState, (UINT32)(-1));
+ }
+ }
+ }
+ catch (GcInfoNoMemoryException& e)
+ {
+ 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) // Encoe 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;
+ 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;
+ 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
+
+ }
+
+ //-----------------------------------------------------------------
+ // Encode EH clauses and bit vectors
+ //-----------------------------------------------------------------
+
+#ifndef DISABLE_EH_VECTORS
+ GCINFO_WRITE_VARL_U(m_Info1, numUsedEHClauses, NUM_EH_CLAUSES_ENCBASE, NumEhSize);
+
+ basePos = 0;
+ for(UINT32 clauseIndex = 0; clauseIndex < numEHClauses; clauseIndex++)
+ {
+ EE_ILEXCEPTION_CLAUSE * pClause;
+ pClause = pEHInfo->EHClause(clauseIndex);
+
+ if(!IsEssential(pClause))
+ continue;
+
+ UINT32 normStartOffset = NORMALIZE_CODE_OFFSET(pClause->TryStartPC);
+ UINT32 normStopOffset = NORMALIZE_CODE_OFFSET(pClause->TryEndPC);
+ _ASSERTE(normStopOffset > normStartOffset);
+
+ GCINFO_WRITE(m_Info1, normStartOffset, numBitsPerOffset, EhPosSize);
+ GCINFO_WRITE(m_Info1, normStopOffset - 1, numBitsPerOffset, EhPosSize);
+
+ for(UINT slotIndex = 0; slotIndex < m_NumSlots; slotIndex++)
+ {
+ if(!m_SlotTable[slotIndex].IsDeleted())
+ {
+ GCINFO_WRITE(m_Info1, ehLiveSlots.ReadBit(basePos + slotIndex) ? 1 : 0, 1, EhStateSize);
+ }
+ }
+ basePos += m_NumSlots;
+ }
+#endif // DISABLE_EH_VECTORS
+#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++)
+ {
+ size_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
+ qsort(
+ pCurrent - numTransitionsInCurrentChunk,
+ numTransitionsInCurrentChunk,
+ sizeof(LifetimeTransition),
+ 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;
+#ifdef DISABLE_EH_VECTORS
+ m_CurrentMethodSize.NumEh = 0;
+#else
+ m_CurrentMethodSize.NumEh = numUsedEHClauses;
+#endif
+#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: %Iu\n", g_NumSlimHeaders);
+ LogSpew(LF_GCINFO, LL_INFO10, "NumMethods: %Iu\n", g_NumFatHeaders);
+#endif
+}
+
+void GcInfoEncoder::SizeofSlotStateVarLengthVector(const BitArray &vector,
+ UINT32 baseSkip,
+ UINT32 baseRun,
+ UINT32 *pSizeofSimple,
+ UINT32 *pSizeofRLE,
+ UINT32 *pSizeofRLENeg)
+{
+ // Try 3 different encodings
+ UINT32 sizeofSimple = 1;
+ UINT32 sizeofRLE;
+ UINT32 sizeofRLENeg;
+ for(UINT32 i = 0; i < m_NumSlots && !m_SlotTable[i].IsUntracked(); i++)
+ {
+ if(!m_SlotTable[i].IsDeleted())
+ sizeofSimple++;
+ }
+
+ if (sizeofSimple <= 2 + baseSkip + 1 + baseRun + 1)
+ {
+ // simple encoding is smaller than the smallest of the others
+ // without even trying
+ sizeofRLE = sizeofSimple + 1;
+ sizeofRLENeg = sizeofSimple + 1;
+ }
+ else
+ {
+ sizeofRLE = 2; // For the header
+ sizeofRLENeg = 2;
+
+ UINT32 rleStart = 0;
+ bool fPrev = false;
+ UINT32 i;
+ for(i = 0; i < m_NumSlots && !m_SlotTable[i].IsUntracked(); i++)
+ {
+ if(!m_SlotTable[i].IsDeleted())
+ {
+ if (vector.ReadBit(i))
+ {
+ if (!fPrev)
+ {
+ // Skipping is done
+ sizeofRLE += BitStreamWriter::SizeofVarLengthUnsigned(i - rleStart, baseSkip);
+ sizeofRLENeg += BitStreamWriter::SizeofVarLengthUnsigned(i - rleStart, baseRun);
+ rleStart = i + 1;
+ fPrev = true;
+ }
+ }
+ else
+ {
+ if (fPrev)
+ {
+ // Run is done
+ sizeofRLE += BitStreamWriter::SizeofVarLengthUnsigned(i - rleStart, baseRun);
+ sizeofRLENeg += BitStreamWriter::SizeofVarLengthUnsigned(i - rleStart, baseSkip);
+ rleStart = i + 1;
+ fPrev = false;
+ }
+ }
+ }
+ else
+ {
+ rleStart++;
+ }
+ }
+
+ _ASSERTE(i >= rleStart);
+ sizeofRLE += BitStreamWriter::SizeofVarLengthUnsigned(i - rleStart, fPrev ? baseRun : baseSkip);
+ sizeofRLENeg += BitStreamWriter::SizeofVarLengthUnsigned(i - rleStart, fPrev ? baseSkip : baseRun);
+ }
+
+ *pSizeofSimple = sizeofSimple;
+ *pSizeofRLE = sizeofRLE;
+ *pSizeofRLENeg = sizeofRLENeg;
+}
+
+UINT32 GcInfoEncoder::SizeofSlotStateVarLengthVector(const BitArray &vector,
+ UINT32 baseSkip,
+ UINT32 baseRun)
+{
+ // Try 3 different encodings
+ UINT32 sizeofSimple;
+ UINT32 sizeofRLE;
+ UINT32 sizeofRLENeg;
+ SizeofSlotStateVarLengthVector(vector, baseSkip, baseRun, &sizeofSimple, &sizeofRLE, &sizeofRLENeg);
+
+ if (sizeofSimple <= sizeofRLE && sizeofSimple <= sizeofRLENeg)
+ return sizeofSimple;
+ if (sizeofRLE <= sizeofRLENeg)
+ return sizeofRLE;
+ return sizeofRLENeg;
+}
+
+UINT32 GcInfoEncoder::WriteSlotStateVarLengthVector(BitStreamWriter &writer,
+ const BitArray &vector,
+ UINT32 baseSkip,
+ UINT32 baseRun)
+{
+ // Try 3 different encodings
+ UINT32 sizeofSimple;
+ UINT32 sizeofRLE;
+ UINT32 sizeofRLENeg;
+ SizeofSlotStateVarLengthVector(vector, baseSkip, baseRun, &sizeofSimple, &sizeofRLE, &sizeofRLENeg);
+ UINT32 result;
+
+#ifdef _DEBUG
+ size_t initial = writer.GetBitCount();
+#endif // _DEBUG
+
+ if (sizeofSimple <= sizeofRLE && sizeofSimple <= sizeofRLENeg)
+ {
+ // Simple encoding is smallest
+ writer.Write(0, 1);
+ WriteSlotStateVector(writer, vector);
+ result = sizeofSimple;
+ }
+ else
+ {
+ // One of the RLE encodings is the smallest
+ writer.Write(1, 1);
+
+ if (sizeofRLENeg < sizeofRLE)
+ {
+ writer.Write(1, 1);
+ UINT32 swap = baseSkip;
+ baseSkip = baseRun;
+ baseRun = swap;
+ result = sizeofRLENeg;
+ }
+ else
+ {
+ writer.Write(0, 1);
+ result = sizeofRLE;
+ }
+
+
+ UINT32 rleStart = 0;
+ UINT32 i;
+ bool fPrev = false;
+ for(i = 0; i < m_NumSlots && !m_SlotTable[i].IsUntracked(); i++)
+ {
+ if(!m_SlotTable[i].IsDeleted())
+ {
+
+ if (vector.ReadBit(i))
+ {
+ if (!fPrev)
+ {
+ // Skipping is done
+ writer.EncodeVarLengthUnsigned(i - rleStart, baseSkip);
+ rleStart = i + 1;
+ fPrev = true;
+ }
+ }
+ else
+ {
+ if (fPrev)
+ {
+ // Run is done
+ writer.EncodeVarLengthUnsigned(i - rleStart, baseRun);
+ rleStart = i + 1;
+ fPrev = false;
+ }
+ }
+ }
+ else
+ {
+ rleStart++;
+ }
+ }
+
+ _ASSERTE(i >= rleStart);
+ writer.EncodeVarLengthUnsigned(i - rleStart, fPrev ? baseRun : baseSkip);
+ }
+
+#ifdef _DEBUG
+ _ASSERTE(result + initial == writer.GetBitCount());
+#endif // _DEBUG
+
+ return result;
+}
+
+
+void GcInfoEncoder::EliminateRedundantLiveDeadPairs(LifetimeTransition** ppTransitions,
+ size_t* pNumTransitions,
+ LifetimeTransition** ppEndTransitions)
+{
+ LifetimeTransition* pTransitions = *ppTransitions;
+ LifetimeTransition* pEndTransitions = *ppEndTransitions;
+
+ LifetimeTransition* pNewTransitions = NULL;
+ LifetimeTransition* pNewTransitionsCopyPtr = NULL;
+ for (LifetimeTransition* pCurrent = pTransitions; pCurrent < pEndTransitions; pCurrent++)
+ {
+ // Is pCurrent the first of a dead/live pair?
+ LifetimeTransition* pNext = pCurrent + 1;
+ if (pNext < pEndTransitions &&
+ pCurrent->CodeOffset == pNext->CodeOffset &&
+ pCurrent->SlotId == pNext->SlotId &&
+ pCurrent->IsDeleted == pNext->IsDeleted &&
+ pCurrent->BecomesLive != pNext->BecomesLive)
+ {
+ // They are a pair we want to delete. If this is the first pair we've encountered, allocate
+ // the new array:
+ if (pNewTransitions == NULL)
+ {
+ pNewTransitions = (LifetimeTransition*)m_pAllocator->Alloc((*pNumTransitions) * sizeof(LifetimeTransition));
+ pNewTransitionsCopyPtr = pNewTransitions;
+ // Copy from the start up to (but not including) pCurrent...
+ for (LifetimeTransition* pCopyPtr = pTransitions; pCopyPtr < pCurrent; pCopyPtr++, pNewTransitionsCopyPtr++)
+ *pNewTransitionsCopyPtr = *pCopyPtr;
+ }
+ pCurrent++;
+ }
+ else
+ {
+ // pCurrent is not part of a pair. If we're copying, copy.
+ if (pNewTransitionsCopyPtr != NULL)
+ {
+ *pNewTransitionsCopyPtr++ = *pCurrent;
+ }
+ }
+ }
+ // If we deleted any pairs, substitute the new array for the old.
+ if (pNewTransitions != NULL)
+ {
+ m_pAllocator->Free(pTransitions);
+ *ppTransitions = pNewTransitions;
+ assert(pNewTransitionsCopyPtr != NULL);
+ *ppEndTransitions = pNewTransitionsCopyPtr;
+ *pNumTransitions = (*ppEndTransitions) - (*ppTransitions);
+ }
+}
+
+//
+// Write encoded information to its final destination and frees temporary buffers.
+// The encoder shouldn't be used anymore after calling this method.
+//
+BYTE* GcInfoEncoder::Emit()
+{
+ size_t cbGcInfoSize = m_Info1.GetByteCount() +
+ m_Info2.GetByteCount();
+
+ LOG((LF_GCINFO, LL_INFO100, "GcInfoEncoder::Emit(): Size of GC info is %u bytes, code size %u bytes.\n", (unsigned)cbGcInfoSize, m_CodeLength ));
+
+ BYTE* destBuffer = (BYTE *)eeAllocGCInfo(cbGcInfoSize);
+ // Allocator will throw an exception on failure.
+ // NOTE: the returned pointer may not be aligned during ngen.
+ _ASSERTE( destBuffer );
+
+ BYTE* ptr = destBuffer;
+
+ m_Info1.CopyTo( ptr );
+ ptr += m_Info1.GetByteCount();
+ m_Info1.Dispose();
+
+ m_Info2.CopyTo( ptr );
+ ptr += m_Info2.GetByteCount();
+ m_Info2.Dispose();
+
+#ifdef MUST_CALL_JITALLOCATOR_FREE
+ m_pAllocator->Free( m_SlotTable );
+#endif
+
+ return destBuffer;
+}
+
+void * GcInfoEncoder::eeAllocGCInfo (size_t blockSize)
+{
+ return m_pCorJitInfo->allocGCInfo(blockSize);
+}
+
+
+BitStreamWriter::BitStreamWriter( IAllocator* pAllocator )
+{
+ m_pAllocator = pAllocator;
+ m_BitCount = 0;
+#ifdef _DEBUG
+ m_MemoryBlocksCount = 0;
+#endif
+
+ // Allocate memory blocks lazily
+ m_OutOfBlockSlot = m_pCurrentSlot = (size_t*) NULL;
+ m_FreeBitsInCurrentSlot = 0;
+}
+
+//
+// bit 0 is the least significative bit
+// The stream encodes the first come bit in the least significant bit of each byte
+//
+void BitStreamWriter::Write( size_t data, UINT32 count )
+{
+ _ASSERT(count <= BITS_PER_SIZE_T);
+
+ if(count)
+ {
+ // Increment it now as we change count later on
+ m_BitCount += count;
+
+ if( count > m_FreeBitsInCurrentSlot )
+ {
+ if( m_FreeBitsInCurrentSlot > 0 )
+ {
+ _ASSERTE(m_FreeBitsInCurrentSlot < BITS_PER_SIZE_T);
+ WriteInCurrentSlot( data, m_FreeBitsInCurrentSlot );
+ count -= m_FreeBitsInCurrentSlot;
+ data >>= m_FreeBitsInCurrentSlot;
+ }
+
+ _ASSERTE( count > 0 );
+
+ // Initialize the next slot
+ if( ++m_pCurrentSlot >= m_OutOfBlockSlot )
+ {
+ // Get a new memory block
+ AllocMemoryBlock();
+ }
+
+ InitCurrentSlot();
+
+ // Write the remainder
+ WriteInCurrentSlot( data, count );
+ m_FreeBitsInCurrentSlot -= count;
+ }
+ else
+ {
+ WriteInCurrentSlot( data, count );
+ m_FreeBitsInCurrentSlot -= count;
+ // if m_FreeBitsInCurrentSlot becomes 0 a nwe slot will initialized on the next request
+ }
+ }
+}
+
+
+void BitStreamWriter::CopyTo( BYTE* buffer )
+{
+ int i,c;
+ BYTE* source = NULL;
+
+ MemoryBlock* pMemBlock = m_MemoryBlocks.Head();
+ if( pMemBlock == NULL )
+ return;
+
+ while (pMemBlock->Next() != NULL)
+ {
+ source = (BYTE*) pMemBlock->Contents;
+ // @TODO: use memcpy instead
+ for( i = 0; i < m_MemoryBlockSize; i++ )
+ {
+ *( buffer++ ) = *( source++ );
+ }
+
+ pMemBlock = pMemBlock->Next();
+ }
+
+ source = (BYTE*) pMemBlock->Contents;
+ // The number of bytes to copy in the last block
+ c = (int) ((BYTE*) ( m_pCurrentSlot + 1 ) - source - m_FreeBitsInCurrentSlot/8);
+ _ASSERTE( c >= 0 );
+ // @TODO: use memcpy instead
+ for( i = 0; i < c; i++ )
+ {
+ *( buffer++ ) = *( source++ );
+ }
+
+}
+
+void BitStreamWriter::Dispose()
+{
+ m_MemoryBlocks.Dispose(m_pAllocator);
+}
+
+int BitStreamWriter::SizeofVarLengthUnsigned( size_t n, UINT32 base)
+{
+ // If a value gets so big we are probably doing something wrong
+ _ASSERTE(((INT32)(UINT32)n) >= 0);
+ _ASSERTE((base > 0) && (base < BITS_PER_SIZE_T));
+ size_t numEncodings = 1 << base;
+ int bitsUsed;
+ for(bitsUsed = base+1; ; bitsUsed += base+1)
+ {
+ if( n < numEncodings )
+ {
+ return bitsUsed;
+ }
+ else
+ {
+ n >>= base;
+ }
+ }
+ return bitsUsed;
+}
+
+int BitStreamWriter::EncodeVarLengthUnsigned( size_t n, UINT32 base)
+{
+ // If a value gets so big we are probably doing something wrong
+ _ASSERTE(((INT32)(UINT32)n) >= 0);
+ _ASSERTE((base > 0) && (base < BITS_PER_SIZE_T));
+ size_t numEncodings = 1 << base;
+ int bitsUsed;
+ for(bitsUsed = base+1; ; bitsUsed += base+1)
+ {
+ if( n < numEncodings )
+ {
+ Write( n, base+1 ); // This sets the extension bit to zero
+ return bitsUsed;
+ }
+ else
+ {
+ size_t currentChunk = n & (numEncodings-1);
+ Write( currentChunk | numEncodings, base+1 );
+ n >>= base;
+ }
+ }
+ return bitsUsed;
+}
+
+int BitStreamWriter::EncodeVarLengthSigned( SSIZE_T n, UINT32 base )
+{
+ _ASSERTE((base > 0) && (base < BITS_PER_SIZE_T));
+ size_t numEncodings = 1 << base;
+ for(int bitsUsed = base+1; ; bitsUsed += base+1)
+ {
+ size_t currentChunk = ((size_t) n) & (numEncodings-1);
+ size_t topmostBit = currentChunk & (numEncodings >> 1);
+ n >>= base; // signed arithmetic shift
+ if((topmostBit && (n == (SSIZE_T)-1)) || (!topmostBit && (n == 0)))
+ {
+ // The topmost bit correctly represents the sign
+ Write( currentChunk, base+1 ); // This sets the extension bit to zero
+ return bitsUsed;
+ }
+ else
+ {
+ Write( currentChunk | numEncodings, base+1 );
+ }
+ }
+}
+