1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*****************************************************************************
6 *
7 * GC Information Encoding API
8 *
9 */
10
11#include <stdint.h>
12
13#include "gcinfoencoder.h"
14
15#ifdef _DEBUG
16 #ifndef LOGGING
17 #define LOGGING
18 #endif
19#endif
20
21#ifndef STANDALONE_BUILD
22#include "log.h"
23#include "simplerhash.h"
24#include "bitposition.h"
25#endif
26
27#ifdef MEASURE_GCINFO
28#define GCINFO_WRITE(writer, val, numBits, counter) \
29 { \
30 writer.Write(val, numBits); \
31 m_CurrentMethodSize.counter += numBits; \
32 m_CurrentMethodSize.TotalSize += numBits; \
33 }
34#define GCINFO_WRITE_VARL_U(writer, val, base, counter) \
35 { \
36 size_t __temp = \
37 writer.EncodeVarLengthUnsigned(val, base); \
38 m_CurrentMethodSize.counter += __temp; \
39 m_CurrentMethodSize.TotalSize += __temp; \
40 }
41#define GCINFO_WRITE_VARL_S(writer, val, base, counter) \
42 { \
43 size_t __temp = \
44 writer.EncodeVarLengthSigned(val, base); \
45 m_CurrentMethodSize.counter += __temp; \
46 m_CurrentMethodSize.TotalSize += __temp; \
47 }
48#define GCINFO_WRITE_VECTOR(writer, vector, counter) \
49 { \
50 WriteSlotStateVector(writer, vector); \
51 for(UINT32 i = 0; i < m_NumSlots; i++) \
52 { \
53 if(!m_SlotTable[i].IsDeleted() && \
54 !m_SlotTable[i].IsUntracked()) \
55 { \
56 m_CurrentMethodSize.counter++; \
57 m_CurrentMethodSize.TotalSize++; \
58 } \
59 } \
60 }
61#define GCINFO_WRITE_VAR_VECTOR(writer, vector, baseSkip, baseRun, counter) \
62 { \
63 size_t __temp = \
64 WriteSlotStateVarLengthVector(writer, vector, baseSkip, baseRun); \
65 m_CurrentMethodSize.counter += __temp; \
66 m_CurrentMethodSize.TotalSize += __temp; \
67 }
68#else
69#define GCINFO_WRITE(writer, val, numBits, counter) \
70 writer.Write(val, numBits);
71
72#define GCINFO_WRITE_VARL_U(writer, val, base, counter) \
73 writer.EncodeVarLengthUnsigned(val, base);
74
75#define GCINFO_WRITE_VARL_S(writer, val, base, counter) \
76 writer.EncodeVarLengthSigned(val, base);
77
78#define GCINFO_WRITE_VECTOR(writer, vector, counter) \
79 WriteSlotStateVector(writer, vector);
80
81#define GCINFO_WRITE_VAR_VECTOR(writer, vector, baseSkip, baseRun, counter) \
82 WriteSlotStateVarLengthVector(writer, vector, baseSkip, baseRun);
83#endif
84
85#define LOG_GCSLOTDESC_FMT "%s%c%d%s%s%s"
86#define LOG_GCSLOTDESC_ARGS(pDesc) (pDesc)->IsRegister() ? "register" \
87 : GcStackSlotBaseNames[(pDesc)->Slot.Stack.Base], \
88 (pDesc)->IsRegister() ? ' ' : (pDesc)->Slot.Stack.SpOffset < 0 ? '-' : '+', \
89 (pDesc)->IsRegister() ? (pDesc)->Slot.RegisterNumber \
90 : ((pDesc)->Slot.Stack.SpOffset), \
91 (pDesc)->IsPinned() ? " pinned" : "", \
92 (pDesc)->IsInterior() ? " interior" : "", \
93 (pDesc)->IsUntracked() ? " untracked" : ""
94
95#define LOG_REGTRANSITION_FMT "register %u%s%s"
96#define LOG_REGTRANSITION_ARGS(RegisterNumber, Flags) \
97 RegisterNumber, \
98 (Flags & GC_SLOT_PINNED) ? " pinned" : "", \
99 (Flags & GC_SLOT_INTERIOR) ? " interior" : ""
100
101#define LOG_STACKTRANSITION_FMT "%s%c%d%s%s%s"
102#define LOG_STACKTRANSITION_ARGS(BaseRegister, StackOffset, Flags) \
103 GcStackSlotBaseNames[BaseRegister], \
104 ((StackOffset) < 0) ? '-' : '+', \
105 ((StackOffset) >= 0) ? (StackOffset) \
106 : -(StackOffset), \
107 (Flags & GC_SLOT_PINNED) ? " pinned" : "", \
108 (Flags & GC_SLOT_INTERIOR) ? " interior" : "", \
109 (Flags & GC_SLOT_UNTRACKED) ? " untracked" : ""
110
111class BitArray
112{
113 friend class BitArrayIterator;
114 typedef uint32_t ChunkType;
115 static constexpr size_t NumBitsPerChunk = sizeof(ChunkType) * CHAR_BIT;
116public:
117 BitArray(IAllocator* pJitAllocator, size_t numBits)
118 {
119 const size_t numChunks = (numBits + NumBitsPerChunk - 1) / NumBitsPerChunk;
120 m_pData = (ChunkType*)pJitAllocator->Alloc(sizeof(ChunkType) * numChunks);
121 m_pEndData = m_pData + numChunks;
122#ifdef MUST_CALL_IALLOCATOR_FREE
123 m_pJitAllocator = pJitAllocator;
124#endif
125 }
126
127 inline void SetBit( size_t pos )
128 {
129 size_t element = pos / NumBitsPerChunk;
130 int bpos = (int)(pos % NumBitsPerChunk);
131 m_pData[element] |= ((ChunkType)1 << bpos);
132 }
133
134 inline void ClearBit( size_t pos )
135 {
136 size_t element = pos / NumBitsPerChunk;
137 int bpos = (int)(pos % NumBitsPerChunk);
138 m_pData[element] &= ~((ChunkType)1 << bpos);
139 }
140
141 inline void SetAll()
142 {
143 ChunkType* ptr = m_pData;
144 while(ptr < m_pEndData)
145 *(ptr++) = ~(ChunkType)0;
146 }
147
148 inline void ClearAll()
149 {
150 ChunkType* ptr = m_pData;
151 while(ptr < m_pEndData)
152 *(ptr++) = (ChunkType)0;
153 }
154
155 inline void WriteBit( size_t pos, BOOL val)
156 {
157 if(val)
158 SetBit(pos);
159 else
160 ClearBit(pos);
161 }
162
163 inline uint32_t ReadBit( size_t pos ) const
164 {
165 size_t element = pos / NumBitsPerChunk;
166 int bpos = (int)(pos % NumBitsPerChunk);
167 return (m_pData[element] & ((ChunkType)1 << bpos));
168 }
169
170 inline bool operator==(const BitArray &other) const
171 {
172 _ASSERTE(other.m_pEndData - other.m_pData == m_pEndData - m_pData);
173 ChunkType* dest = m_pData;
174 ChunkType* src = other.m_pData;
175 return 0 == memcmp(dest, src, (m_pEndData - m_pData) * sizeof(ChunkType));
176 }
177
178 inline int GetHashCode() const
179 {
180 const int* src = (const int*)m_pData;
181 int result = *src++;
182 while (src < (const int*)m_pEndData)
183 result = _rotr(result, 5) ^ *src++;
184 return result;
185 }
186
187 inline BitArray& operator=(const BitArray &other)
188 {
189 _ASSERTE(other.m_pEndData - other.m_pData == m_pEndData - m_pData);
190 ChunkType* dest = m_pData;
191 ChunkType* src = other.m_pData;
192 while(dest < m_pEndData)
193 *(dest++) = *(src++);
194
195 return *this;
196 }
197
198 inline BitArray& operator|=(const BitArray &other)
199 {
200 _ASSERTE(other.m_pEndData - other.m_pData == m_pEndData - m_pData);
201 ChunkType* dest = m_pData;
202 ChunkType* src = other.m_pData;
203 while(dest < m_pEndData)
204 *(dest++) |= *(src++);
205
206 return *this;
207 }
208
209#ifdef MUST_CALL_IALLOCATOR_FREE
210 ~BitArray()
211 {
212 m_pAllocator->Free( m_pData );
213 }
214#endif
215
216 static void* operator new(size_t size, IAllocator* allocator)
217 {
218 return allocator->Alloc(size);
219 }
220
221private:
222 ChunkType * m_pData;
223 ChunkType * m_pEndData;
224#ifdef MUST_CALL_IALLOCATOR_FREE
225 IAllocator* m_pJitAllocator;
226#endif
227};
228
229
230class BitArrayIterator
231{
232public:
233 BitArrayIterator(BitArray* bitArray)
234 {
235 m_pCurData = (unsigned *)bitArray->m_pData;
236 m_pEndData = (unsigned *)bitArray->m_pEndData;
237 m_curBits = *m_pCurData;
238 m_curBit = 0;
239 m_curBase = 0;
240 GetNext();
241 }
242 void operator++(int dummy) //int dummy is c++ for "this is postfix ++"
243 {
244 GetNext();
245 }
246
247 void operator++() // prefix ++
248 {
249 GetNext();
250 }
251 void GetNext()
252 {
253 m_curBits -= m_curBit;
254 while (m_curBits == 0)
255 {
256 m_pCurData++;
257 m_curBase += 32;
258 if (m_pCurData == m_pEndData)
259 break;
260 m_curBits = *m_pCurData;
261 }
262 m_curBit = (unsigned)((int)m_curBits & -(int)m_curBits);
263 }
264 unsigned operator*()
265 {
266 assert(!end() && (m_curBit != 0));
267 unsigned bitPosition = BitPosition(m_curBit);
268 return bitPosition + m_curBase;
269 }
270 bool end()
271 {
272 return (m_pCurData == m_pEndData);
273 }
274private:
275 unsigned* m_pCurData;
276 unsigned* m_pEndData;
277 unsigned m_curBits;
278 unsigned m_curBit;
279 unsigned m_curBase;
280};
281
282class LiveStateFuncs
283{
284public:
285 static int GetHashCode(const BitArray * key)
286 {
287 return key->GetHashCode();
288 }
289
290 static bool Equals(const BitArray * k1, const BitArray * k2)
291 {
292 return *k1 == *k2;
293 }
294};
295
296class GcInfoNoMemoryException
297{
298};
299
300class GcInfoHashBehavior
301{
302public:
303 static const unsigned s_growth_factor_numerator = 3;
304 static const unsigned s_growth_factor_denominator = 2;
305
306 static const unsigned s_density_factor_numerator = 3;
307 static const unsigned s_density_factor_denominator = 4;
308
309 static const unsigned s_minimum_allocation = 7;
310
311 inline static void DECLSPEC_NORETURN NoMemory()
312 {
313 throw GcInfoNoMemoryException();
314 }
315};
316
317typedef SimplerHashTable<const BitArray *, LiveStateFuncs, UINT32, GcInfoHashBehavior> LiveStateHashTable;
318
319#ifdef MEASURE_GCINFO
320// Fi = fully-interruptible; we count any method that has one or more interruptible ranges
321// Pi = partially-interruptible; methods with zero fully-interruptible ranges
322GcInfoSize g_FiGcInfoSize;
323GcInfoSize g_PiGcInfoSize;
324// Number of methods with GcInfo that have SlimHeader
325size_t g_NumSlimHeaders = 0;
326// Number of methods with GcInfo that have FatHeader
327size_t g_NumFatHeaders = 0;
328
329GcInfoSize::GcInfoSize()
330{
331 memset(this, 0, sizeof(*this));
332}
333
334GcInfoSize& GcInfoSize::operator+=(const GcInfoSize& other)
335{
336 TotalSize += other.TotalSize;
337
338 NumMethods += other.NumMethods;
339 NumCallSites += other.NumCallSites;
340 NumRanges += other.NumRanges;
341 NumRegs += other.NumRegs;
342 NumStack += other.NumStack;
343 NumUntracked += other.NumUntracked;
344 NumTransitions += other.NumTransitions;
345 SizeOfCode += other.SizeOfCode;
346 EncPreservedSlots += other.EncPreservedSlots;
347
348 UntrackedSlotSize += other.UntrackedSlotSize;
349 NumUntrackedSize += other.NumUntrackedSize;
350 FlagsSize += other.FlagsSize;
351 CodeLengthSize += other.CodeLengthSize;
352 ProEpilogSize += other.ProEpilogSize;
353 SecObjSize += other.SecObjSize;
354 GsCookieSize += other.GsCookieSize;
355 GenericsCtxSize += other.GenericsCtxSize;
356 PspSymSize += other.PspSymSize;
357 StackBaseSize += other.StackBaseSize;
358 ReversePInvokeFrameSize += other.ReversePInvokeFrameSize;
359 FixedAreaSize += other.FixedAreaSize;
360 NumCallSitesSize += other.NumCallSitesSize;
361 NumRangesSize += other.NumRangesSize;
362 CallSitePosSize += other.CallSitePosSize;
363 RangeSize += other.RangeSize;
364 NumRegsSize += other.NumRegsSize;
365 NumStackSize += other.NumStackSize;
366 RegSlotSize += other.RegSlotSize;
367 StackSlotSize += other.StackSlotSize;
368 CallSiteStateSize += other.CallSiteStateSize;
369 EhPosSize += other.EhPosSize;
370 EhStateSize += other.EhStateSize;
371 ChunkPtrSize += other.ChunkPtrSize;
372 ChunkMaskSize += other.ChunkMaskSize;
373 ChunkFinalStateSize += other.ChunkFinalStateSize;
374 ChunkTransitionSize += other.ChunkTransitionSize;
375
376 return *this;
377}
378
379void GcInfoSize::Log(DWORD level, const char * header)
380{
381 if(LoggingOn(LF_GCINFO, level))
382 {
383 LogSpew(LF_GCINFO, level, header);
384
385 LogSpew(LF_GCINFO, level, "---COUNTS---\n");
386 LogSpew(LF_GCINFO, level, "NumMethods: %Iu\n", NumMethods);
387 LogSpew(LF_GCINFO, level, "NumCallSites: %Iu\n", NumCallSites);
388 LogSpew(LF_GCINFO, level, "NumRanges: %Iu\n", NumRanges);
389 LogSpew(LF_GCINFO, level, "NumRegs: %Iu\n", NumRegs);
390 LogSpew(LF_GCINFO, level, "NumStack: %Iu\n", NumStack);
391 LogSpew(LF_GCINFO, level, "NumUntracked: %Iu\n", NumUntracked);
392 LogSpew(LF_GCINFO, level, "NumTransitions: %Iu\n", NumTransitions);
393 LogSpew(LF_GCINFO, level, "SizeOfCode: %Iu\n", SizeOfCode);
394 LogSpew(LF_GCINFO, level, "EncPreservedSlots: %Iu\n", EncPreservedSlots);
395
396 LogSpew(LF_GCINFO, level, "---SIZES(bits)---\n");
397 LogSpew(LF_GCINFO, level, "Total: %Iu\n", TotalSize);
398 LogSpew(LF_GCINFO, level, "UntrackedSlot: %Iu\n", UntrackedSlotSize);
399 LogSpew(LF_GCINFO, level, "NumUntracked: %Iu\n", NumUntrackedSize);
400 LogSpew(LF_GCINFO, level, "Flags: %Iu\n", FlagsSize);
401 LogSpew(LF_GCINFO, level, "CodeLength: %Iu\n", CodeLengthSize);
402 LogSpew(LF_GCINFO, level, "Prolog/Epilog: %Iu\n", ProEpilogSize);
403 LogSpew(LF_GCINFO, level, "SecObj: %Iu\n", SecObjSize);
404 LogSpew(LF_GCINFO, level, "GsCookie: %Iu\n", GsCookieSize);
405 LogSpew(LF_GCINFO, level, "PspSym: %Iu\n", PspSymSize);
406 LogSpew(LF_GCINFO, level, "GenericsCtx: %Iu\n", GenericsCtxSize);
407 LogSpew(LF_GCINFO, level, "StackBase: %Iu\n", StackBaseSize);
408 LogSpew(LF_GCINFO, level, "FixedArea: %Iu\n", FixedAreaSize);
409 LogSpew(LF_GCINFO, level, "ReversePInvokeFrame: %Iu\n", ReversePInvokeFrameSize);
410 LogSpew(LF_GCINFO, level, "NumCallSites: %Iu\n", NumCallSitesSize);
411 LogSpew(LF_GCINFO, level, "NumRanges: %Iu\n", NumRangesSize);
412 LogSpew(LF_GCINFO, level, "CallSiteOffsets: %Iu\n", CallSitePosSize);
413 LogSpew(LF_GCINFO, level, "Ranges: %Iu\n", RangeSize);
414 LogSpew(LF_GCINFO, level, "NumRegs: %Iu\n", NumRegsSize);
415 LogSpew(LF_GCINFO, level, "NumStack: %Iu\n", NumStackSize);
416 LogSpew(LF_GCINFO, level, "RegSlots: %Iu\n", RegSlotSize);
417 LogSpew(LF_GCINFO, level, "StackSlots: %Iu\n", StackSlotSize);
418 LogSpew(LF_GCINFO, level, "CallSiteStates: %Iu\n", CallSiteStateSize);
419 LogSpew(LF_GCINFO, level, "EhOffsets: %Iu\n", EhPosSize);
420 LogSpew(LF_GCINFO, level, "EhStates: %Iu\n", EhStateSize);
421 LogSpew(LF_GCINFO, level, "ChunkPointers: %Iu\n", ChunkPtrSize);
422 LogSpew(LF_GCINFO, level, "ChunkMasks: %Iu\n", ChunkMaskSize);
423 LogSpew(LF_GCINFO, level, "ChunkFinalStates: %Iu\n", ChunkFinalStateSize);
424 LogSpew(LF_GCINFO, level, "Transitions: %Iu\n", ChunkTransitionSize);
425 }
426}
427
428#endif
429
430GcInfoEncoder::GcInfoEncoder(
431 ICorJitInfo* pCorJitInfo,
432 CORINFO_METHOD_INFO* pMethodInfo,
433 IAllocator* pJitAllocator,
434 NoMemoryFunction pNoMem
435 )
436 : m_Info1( pJitAllocator ),
437 m_Info2( pJitAllocator ),
438 m_InterruptibleRanges( pJitAllocator ),
439 m_LifetimeTransitions( pJitAllocator )
440{
441#ifdef MEASURE_GCINFO
442 // This causes multiple complus.log files in JIT64. TODO: consider using ICorJitInfo::logMsg instead.
443 InitializeLogging();
444#endif
445
446 _ASSERTE( pCorJitInfo != NULL );
447 _ASSERTE( pMethodInfo != NULL );
448 _ASSERTE( pJitAllocator != NULL );
449 _ASSERTE( pNoMem != NULL );
450
451 m_pCorJitInfo = pCorJitInfo;
452 m_pMethodInfo = pMethodInfo;
453 m_pAllocator = pJitAllocator;
454 m_pNoMem = pNoMem;
455
456#ifdef _DEBUG
457 CORINFO_METHOD_HANDLE methodHandle = pMethodInfo->ftn;
458
459 // Get the name of the current method along with the enclosing class
460 // or module name.
461 m_MethodName =
462 pCorJitInfo->getMethodName(methodHandle, (const char **)&m_ModuleName);
463#endif
464
465
466 m_SlotTableSize = m_SlotTableInitialSize;
467 m_SlotTable = (GcSlotDesc*) m_pAllocator->Alloc( m_SlotTableSize*sizeof(GcSlotDesc) );
468 m_NumSlots = 0;
469#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
470 m_pCallSites = NULL;
471 m_pCallSiteSizes = NULL;
472 m_NumCallSites = 0;
473#endif
474
475 m_SecurityObjectStackSlot = NO_SECURITY_OBJECT;
476 m_GSCookieStackSlot = NO_GS_COOKIE;
477 m_GSCookieValidRangeStart = 0;
478 _ASSERTE(sizeof(m_GSCookieValidRangeEnd) == sizeof(UINT32));
479 m_GSCookieValidRangeEnd = (UINT32) (-1); // == UINT32.MaxValue
480 m_PSPSymStackSlot = NO_PSP_SYM;
481 m_GenericsInstContextStackSlot = NO_GENERICS_INST_CONTEXT;
482 m_contextParamType = GENERIC_CONTEXTPARAM_NONE;
483
484 m_StackBaseRegister = NO_STACK_BASE_REGISTER;
485 m_SizeOfEditAndContinuePreservedArea = NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA;
486 m_ReversePInvokeFrameSlot = NO_REVERSE_PINVOKE_FRAME;
487#ifdef _TARGET_AMD64_
488 m_WantsReportOnlyLeaf = false;
489#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
490 m_HasTailCalls = false;
491#endif // _TARGET_AMD64_
492 m_IsVarArg = false;
493 m_pLastInterruptibleRange = NULL;
494
495#ifdef _DEBUG
496 m_IsSlotTableFrozen = FALSE;
497#endif //_DEBUG
498
499#ifndef _TARGET_X86_
500 // If the compiler doesn't set the GCInfo, report RT_Unset.
501 // This is used for compatibility with JITs that aren't updated to use the new API.
502 m_ReturnKind = RT_Unset;
503#else
504 m_ReturnKind = RT_Illegal;
505#endif // _TARGET_X86_
506 m_CodeLength = 0;
507#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
508 m_SizeOfStackOutgoingAndScratchArea = -1;
509#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA
510
511}
512
513#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
514void GcInfoEncoder::DefineCallSites(UINT32* pCallSites, BYTE* pCallSiteSizes, UINT32 numCallSites)
515{
516 m_pCallSites = pCallSites;
517 m_pCallSiteSizes = pCallSiteSizes;
518 m_NumCallSites = numCallSites;
519#ifdef _DEBUG
520 for(UINT32 i=0; i<numCallSites; i++)
521 {
522 _ASSERTE(pCallSiteSizes[i] > 0);
523 _ASSERTE(DENORMALIZE_CODE_OFFSET(NORMALIZE_CODE_OFFSET(pCallSites[i])) == pCallSites[i]);
524 if(i > 0)
525 {
526 UINT32 prevEnd = pCallSites[i-1] + pCallSiteSizes[i-1];
527 UINT32 curStart = pCallSites[i];
528 _ASSERTE(curStart >= prevEnd);
529 }
530 }
531#endif
532}
533#endif
534
535GcSlotId GcInfoEncoder::GetRegisterSlotId( UINT32 regNum, GcSlotFlags flags )
536{
537 // We could lookup an existing identical slot in the slot table (via some hashtable mechanism).
538 // We just create duplicates for now.
539
540#ifdef _DEBUG
541 _ASSERTE( !m_IsSlotTableFrozen );
542#endif
543
544 if( m_NumSlots == m_SlotTableSize )
545 {
546 GrowSlotTable();
547 }
548 _ASSERTE( m_NumSlots < m_SlotTableSize );
549
550 _ASSERTE( (flags & (GC_SLOT_IS_REGISTER | GC_SLOT_IS_DELETED | GC_SLOT_UNTRACKED)) == 0 );
551 m_SlotTable[ m_NumSlots ].Slot.RegisterNumber = regNum;
552 m_SlotTable[ m_NumSlots ].Flags = (GcSlotFlags) (flags | GC_SLOT_IS_REGISTER);
553
554 GcSlotId newSlotId;
555 newSlotId = m_NumSlots++;
556
557 return newSlotId;
558}
559
560GcSlotId GcInfoEncoder::GetStackSlotId( INT32 spOffset, GcSlotFlags flags, GcStackSlotBase spBase )
561{
562 // We could lookup an existing identical slot in the slot table (via some hashtable mechanism).
563 // We just create duplicates for now.
564
565#ifdef _DEBUG
566 _ASSERTE( !m_IsSlotTableFrozen );
567#endif
568
569 if( m_NumSlots == m_SlotTableSize )
570 {
571 GrowSlotTable();
572 }
573 _ASSERTE( m_NumSlots < m_SlotTableSize );
574
575 // Not valid to reference anything below the current stack pointer
576 _ASSERTE(GC_SP_REL != spBase || spOffset >= 0);
577
578 _ASSERTE( (flags & (GC_SLOT_IS_REGISTER | GC_SLOT_IS_DELETED)) == 0 );
579
580 // the spOffset for the stack slot is required to be pointer size aligned
581 _ASSERTE((spOffset % TARGET_POINTER_SIZE) == 0);
582
583 m_SlotTable[ m_NumSlots ].Slot.Stack.SpOffset = spOffset;
584 m_SlotTable[ m_NumSlots ].Slot.Stack.Base = spBase;
585 m_SlotTable[ m_NumSlots ].Flags = flags;
586
587 GcSlotId newSlotId;
588 newSlotId = m_NumSlots++;
589
590 return newSlotId;
591}
592
593void GcInfoEncoder::GrowSlotTable()
594{
595 m_SlotTableSize *= 2;
596 GcSlotDesc* newSlotTable = (GcSlotDesc*) m_pAllocator->Alloc( m_SlotTableSize * sizeof(GcSlotDesc) );
597 memcpy( newSlotTable, m_SlotTable, m_NumSlots * sizeof(GcSlotDesc) );
598
599#ifdef MUST_CALL_JITALLOCATOR_FREE
600 m_pAllocator->Free( m_SlotTable );
601#endif
602
603 m_SlotTable = newSlotTable;
604}
605
606void GcInfoEncoder::WriteSlotStateVector(BitStreamWriter &writer, const BitArray& vector)
607{
608 for(UINT32 i = 0; i < m_NumSlots && !m_SlotTable[i].IsUntracked(); i++)
609 {
610 if(!m_SlotTable[i].IsDeleted())
611 writer.Write(vector.ReadBit(i) ? 1 : 0, 1);
612 else
613 _ASSERTE(vector.ReadBit(i) == 0);
614 }
615}
616
617void GcInfoEncoder::DefineInterruptibleRange( UINT32 startInstructionOffset, UINT32 length )
618{
619 UINT32 stopInstructionOffset = startInstructionOffset + length;
620
621 UINT32 normStartOffset = NORMALIZE_CODE_OFFSET(startInstructionOffset);
622 UINT32 normStopOffset = NORMALIZE_CODE_OFFSET(stopInstructionOffset);
623
624 // Ranges must not overlap and must be passed sorted by increasing offset
625 _ASSERTE(
626 m_pLastInterruptibleRange == NULL ||
627 normStartOffset >= m_pLastInterruptibleRange->NormStopOffset
628 );
629
630 // Ignore empty ranges
631 if(normStopOffset > normStartOffset)
632 {
633 if(m_pLastInterruptibleRange
634 && normStartOffset == m_pLastInterruptibleRange->NormStopOffset)
635 {
636 // Merge adjacent ranges
637 m_pLastInterruptibleRange->NormStopOffset = normStopOffset;
638 }
639 else
640 {
641 InterruptibleRange range;
642 range.NormStartOffset = normStartOffset;
643 range.NormStopOffset = normStopOffset;
644 m_pLastInterruptibleRange = m_InterruptibleRanges.Append();
645 *m_pLastInterruptibleRange = range;
646 }
647 }
648
649 LOG((LF_GCINFO, LL_INFO1000000, "interruptible at %x length %x\n", startInstructionOffset, length));
650}
651
652
653
654//
655// For inputs, pass zero as offset
656//
657void GcInfoEncoder::SetSlotState(
658 UINT32 instructionOffset,
659 GcSlotId slotId,
660 GcSlotState slotState
661 )
662{
663 _ASSERTE( (m_SlotTable[ slotId ].Flags & GC_SLOT_UNTRACKED) == 0 );
664
665 LifetimeTransition transition;
666
667 transition.SlotId = slotId;
668 transition.CodeOffset = instructionOffset;
669 transition.BecomesLive = ( slotState == GC_SLOT_LIVE );
670 transition.IsDeleted = FALSE;
671
672 *( m_LifetimeTransitions.Append() ) = transition;
673
674 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));
675}
676
677
678void GcInfoEncoder::SetIsVarArg()
679{
680 m_IsVarArg = true;
681}
682
683void GcInfoEncoder::SetCodeLength( UINT32 length )
684{
685 _ASSERTE( length > 0 );
686 _ASSERTE( m_CodeLength == 0 || m_CodeLength == length );
687 m_CodeLength = length;
688}
689
690
691void GcInfoEncoder::SetSecurityObjectStackSlot( INT32 spOffset )
692{
693 _ASSERTE( spOffset != NO_SECURITY_OBJECT );
694#if defined(_TARGET_AMD64_)
695 _ASSERTE( spOffset < 0x10 && "The security object cannot reside in an input variable!" );
696#endif
697 _ASSERTE( m_SecurityObjectStackSlot == NO_SECURITY_OBJECT || m_SecurityObjectStackSlot == spOffset );
698
699 m_SecurityObjectStackSlot = spOffset;
700}
701
702void GcInfoEncoder::SetPrologSize( UINT32 prologSize )
703{
704 _ASSERTE(prologSize != 0);
705 _ASSERTE(m_GSCookieValidRangeStart == 0 || m_GSCookieValidRangeStart == prologSize);
706 _ASSERTE(m_GSCookieValidRangeEnd == (UINT32)(-1) || m_GSCookieValidRangeEnd == prologSize+1);
707
708 m_GSCookieValidRangeStart = prologSize;
709 // satisfy asserts that assume m_GSCookieValidRangeStart != 0 ==> m_GSCookieValidRangeStart < m_GSCookieValidRangeEnd
710 m_GSCookieValidRangeEnd = prologSize+1;
711}
712
713void GcInfoEncoder::SetGSCookieStackSlot( INT32 spOffsetGSCookie, UINT32 validRangeStart, UINT32 validRangeEnd )
714{
715 _ASSERTE( spOffsetGSCookie != NO_GS_COOKIE );
716 _ASSERTE( m_GSCookieStackSlot == NO_GS_COOKIE || m_GSCookieStackSlot == spOffsetGSCookie );
717 _ASSERTE( validRangeStart < validRangeEnd );
718
719 m_GSCookieStackSlot = spOffsetGSCookie;
720 m_GSCookieValidRangeStart = validRangeStart;
721 m_GSCookieValidRangeEnd = validRangeEnd;
722}
723
724void GcInfoEncoder::SetPSPSymStackSlot( INT32 spOffsetPSPSym )
725{
726 _ASSERTE( spOffsetPSPSym != NO_PSP_SYM );
727 _ASSERTE( m_PSPSymStackSlot == NO_PSP_SYM || m_PSPSymStackSlot == spOffsetPSPSym );
728
729 m_PSPSymStackSlot = spOffsetPSPSym;
730}
731
732void GcInfoEncoder::SetGenericsInstContextStackSlot( INT32 spOffsetGenericsContext, GENERIC_CONTEXTPARAM_TYPE type)
733{
734 _ASSERTE( spOffsetGenericsContext != NO_GENERICS_INST_CONTEXT);
735 _ASSERTE( m_GenericsInstContextStackSlot == NO_GENERICS_INST_CONTEXT || m_GenericsInstContextStackSlot == spOffsetGenericsContext );
736
737 m_GenericsInstContextStackSlot = spOffsetGenericsContext;
738 m_contextParamType = type;
739}
740
741void GcInfoEncoder::SetStackBaseRegister( UINT32 regNum )
742{
743 _ASSERTE( regNum != NO_STACK_BASE_REGISTER );
744 _ASSERTE(DENORMALIZE_STACK_BASE_REGISTER(NORMALIZE_STACK_BASE_REGISTER(regNum)) == regNum);
745 _ASSERTE( m_StackBaseRegister == NO_STACK_BASE_REGISTER || m_StackBaseRegister == regNum );
746 m_StackBaseRegister = regNum;
747}
748
749void GcInfoEncoder::SetSizeOfEditAndContinuePreservedArea( UINT32 slots )
750{
751 _ASSERTE( slots != NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA );
752 _ASSERTE( m_SizeOfEditAndContinuePreservedArea == NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA );
753 m_SizeOfEditAndContinuePreservedArea = slots;
754}
755
756#ifdef _TARGET_AMD64_
757void GcInfoEncoder::SetWantsReportOnlyLeaf()
758{
759 m_WantsReportOnlyLeaf = true;
760}
761#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
762void GcInfoEncoder::SetHasTailCalls()
763{
764 m_HasTailCalls = true;
765}
766#endif // _TARGET_AMD64_
767
768#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
769void GcInfoEncoder::SetSizeOfStackOutgoingAndScratchArea( UINT32 size )
770{
771 _ASSERTE( size != (UINT32)-1 );
772 _ASSERTE( m_SizeOfStackOutgoingAndScratchArea == (UINT32)-1 || m_SizeOfStackOutgoingAndScratchArea == size );
773 m_SizeOfStackOutgoingAndScratchArea = size;
774}
775#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA
776
777void GcInfoEncoder::SetReversePInvokeFrameSlot(INT32 spOffset)
778{
779 m_ReversePInvokeFrameSlot = spOffset;
780}
781
782void GcInfoEncoder::SetReturnKind(ReturnKind returnKind)
783{
784 _ASSERTE(IsValidReturnKind(returnKind));
785
786 m_ReturnKind = returnKind;
787}
788
789struct GcSlotDescAndId
790{
791 GcSlotDesc m_SlotDesc;
792 UINT32 m_SlotId;
793};
794
795int __cdecl CompareSlotDescAndIdBySlotDesc(const void* p1, const void* p2)
796{
797 const GcSlotDesc* pFirst = &reinterpret_cast<const GcSlotDescAndId*>(p1)->m_SlotDesc;
798 const GcSlotDesc* pSecond = &reinterpret_cast<const GcSlotDescAndId*>(p2)->m_SlotDesc;
799
800 int firstFlags = pFirst->Flags ^ GC_SLOT_UNTRACKED;
801 int secondFlags = pSecond->Flags ^ GC_SLOT_UNTRACKED;
802
803 // All registers come before all stack slots
804 // All untracked come last
805 // Then sort them by flags, ensuring that the least-frequent interior/pinned flag combinations are first
806 // This is accomplished in the comparison of flags, since we encode IsRegister in the highest flag bit
807 // And we XOR the UNTRACKED flag to place them last in the second highest flag bit
808 if( firstFlags > secondFlags ) return -1;
809 if( firstFlags < secondFlags ) return 1;
810
811 // Then sort them by slot
812 if( pFirst->IsRegister() )
813 {
814 _ASSERTE( pSecond->IsRegister() );
815 if( pFirst->Slot.RegisterNumber < pSecond->Slot.RegisterNumber ) return -1;
816 if( pFirst->Slot.RegisterNumber > pSecond->Slot.RegisterNumber ) return 1;
817 }
818 else
819 {
820 _ASSERTE( !pSecond->IsRegister() );
821 if( pFirst->Slot.Stack.SpOffset < pSecond->Slot.Stack.SpOffset ) return -1;
822 if( pFirst->Slot.Stack.SpOffset > pSecond->Slot.Stack.SpOffset ) return 1;
823
824 // This is arbitrary, but we want to make sure they are considered separate slots
825 if( pFirst->Slot.Stack.Base < pSecond->Slot.Stack.Base ) return -1;
826 if( pFirst->Slot.Stack.Base > pSecond->Slot.Stack.Base ) return 1;
827 }
828
829 // If we get here, the slots are identical
830 _ASSERTE(!"Duplicate slots definitions found in GC information!");
831 return 0;
832}
833
834
835int __cdecl CompareLifetimeTransitionsByOffsetThenSlot(const void* p1, const void* p2)
836{
837 const GcInfoEncoder::LifetimeTransition* pFirst = (const GcInfoEncoder::LifetimeTransition*) p1;
838 const GcInfoEncoder::LifetimeTransition* pSecond = (const GcInfoEncoder::LifetimeTransition*) p2;
839
840 UINT32 firstOffset = pFirst->CodeOffset;
841 UINT32 secondOffset = pSecond->CodeOffset;
842
843 if (firstOffset == secondOffset)
844 {
845 return pFirst->SlotId - pSecond->SlotId;
846 }
847 else
848 {
849 return firstOffset - secondOffset;
850 }
851}
852
853
854int __cdecl CompareLifetimeTransitionsBySlot(const void* p1, const void* p2)
855{
856 const GcInfoEncoder::LifetimeTransition* pFirst = (const GcInfoEncoder::LifetimeTransition*) p1;
857 const GcInfoEncoder::LifetimeTransition* pSecond = (const GcInfoEncoder::LifetimeTransition*) p2;
858
859 UINT32 firstOffset = pFirst->CodeOffset;
860 UINT32 secondOffset = pSecond->CodeOffset;
861
862 _ASSERTE(GetNormCodeOffsetChunk(firstOffset) == GetNormCodeOffsetChunk(secondOffset));
863
864 // Sort them by slot
865 if( pFirst->SlotId < pSecond->SlotId ) return -1;
866 if( pFirst->SlotId > pSecond->SlotId ) return 1;
867
868 // Then sort them by code offset
869 if( firstOffset < secondOffset )
870 return -1;
871 else
872 {
873 _ASSERTE(( firstOffset > secondOffset ) && "Redundant transitions found in GC info!");
874 return 1;
875 }
876}
877
878BitStreamWriter::MemoryBlockList::MemoryBlockList()
879 : m_head(nullptr),
880 m_tail(nullptr)
881{
882}
883
884BitStreamWriter::MemoryBlock* BitStreamWriter::MemoryBlockList::AppendNew(IAllocator* allocator, size_t bytes)
885{
886 auto* memBlock = reinterpret_cast<MemoryBlock*>(allocator->Alloc(sizeof(MemoryBlock) + bytes));
887 memBlock->m_next = nullptr;
888
889 if (m_tail != nullptr)
890 {
891 _ASSERTE(m_head != nullptr);
892 m_tail->m_next = memBlock;
893 }
894 else
895 {
896 _ASSERTE(m_head == nullptr);
897 m_head = memBlock;
898 }
899
900 m_tail = memBlock;
901 return memBlock;
902}
903
904void BitStreamWriter::MemoryBlockList::Dispose(IAllocator* allocator)
905{
906#ifdef MUST_CALL_JITALLOCATOR_FREE
907 for (MemoryBlock* block = m_head, *next; block != nullptr; block = next)
908 {
909 next = block->m_next;
910 allocator->Free(block);
911 }
912 m_head = nullptr;
913 m_tail = nullptr;
914#endif
915}
916
917void GcInfoEncoder::FinalizeSlotIds()
918{
919#ifdef _DEBUG
920 m_IsSlotTableFrozen = TRUE;
921#endif
922}
923
924bool GcInfoEncoder::IsAlwaysScratch(GcSlotDesc &slotDesc)
925{
926#if defined(_TARGET_ARM_)
927
928 _ASSERTE( m_SizeOfStackOutgoingAndScratchArea != (UINT32)-1 );
929 if(slotDesc.IsRegister())
930 {
931 int regNum = (int) slotDesc.Slot.RegisterNumber;
932 _ASSERTE(regNum >= 0 && regNum <= 14);
933 _ASSERTE(regNum != 13); // sp
934
935 return ((regNum <= 3) || (regNum >= 12)); // R12 and R14/LR are both scratch registers
936 }
937 else if (!slotDesc.IsUntracked() && (slotDesc.Slot.Stack.Base == GC_SP_REL) &&
938 ((UINT32)slotDesc.Slot.Stack.SpOffset < m_SizeOfStackOutgoingAndScratchArea))
939 {
940 return TRUE;
941 }
942 else
943 return FALSE;
944
945#elif defined(_TARGET_AMD64_)
946
947 _ASSERTE( m_SizeOfStackOutgoingAndScratchArea != (UINT32)-1 );
948 if(slotDesc.IsRegister())
949 {
950 int regNum = (int) slotDesc.Slot.RegisterNumber;
951 _ASSERTE(regNum >= 0 && regNum <= 16);
952 _ASSERTE(regNum != 4); // rsp
953
954 UINT16 PreservedRegMask =
955 (1 << 3) // rbx
956 | (1 << 5) // rbp
957#ifndef UNIX_AMD64_ABI
958 | (1 << 6) // rsi
959 | (1 << 7) // rdi
960#endif // UNIX_AMD64_ABI
961 | (1 << 12) // r12
962 | (1 << 13) // r13
963 | (1 << 14) // r14
964 | (1 << 15); // r15
965
966 return !(PreservedRegMask & (1 << regNum));
967 }
968 else if (!slotDesc.IsUntracked() && (slotDesc.Slot.Stack.Base == GC_SP_REL) &&
969 ((UINT32)slotDesc.Slot.Stack.SpOffset < m_SizeOfStackOutgoingAndScratchArea))
970 {
971 return TRUE;
972 }
973 else
974 return FALSE;
975
976#else
977 return FALSE;
978#endif
979}
980
981void GcInfoEncoder::Build()
982{
983#ifdef _DEBUG
984 _ASSERTE(m_IsSlotTableFrozen || m_NumSlots == 0);
985#endif
986
987 _ASSERTE((1 << NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2) == NUM_NORM_CODE_OFFSETS_PER_CHUNK);
988
989 LOG((LF_GCINFO, LL_INFO100,
990 "Entering GcInfoEncoder::Build() for method %s[%s]\n",
991 m_MethodName, m_ModuleName
992 ));
993
994
995 ///////////////////////////////////////////////////////////////////////
996 // Method header
997 ///////////////////////////////////////////////////////////////////////
998
999
1000 UINT32 hasSecurityObject = (m_SecurityObjectStackSlot != NO_SECURITY_OBJECT);
1001 UINT32 hasGSCookie = (m_GSCookieStackSlot != NO_GS_COOKIE);
1002 UINT32 hasContextParamType = (m_GenericsInstContextStackSlot != NO_GENERICS_INST_CONTEXT);
1003 UINT32 hasReversePInvokeFrame = (m_ReversePInvokeFrameSlot != NO_REVERSE_PINVOKE_FRAME);
1004
1005 BOOL slimHeader = (!m_IsVarArg && !hasSecurityObject && !hasGSCookie && (m_PSPSymStackSlot == NO_PSP_SYM) &&
1006 !hasContextParamType && (m_InterruptibleRanges.Count() == 0) && !hasReversePInvokeFrame &&
1007 ((m_StackBaseRegister == NO_STACK_BASE_REGISTER) || (NORMALIZE_STACK_BASE_REGISTER(m_StackBaseRegister) == 0))) &&
1008 (m_SizeOfEditAndContinuePreservedArea == NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA) &&
1009#ifdef _TARGET_AMD64_
1010 !m_WantsReportOnlyLeaf &&
1011#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
1012 !m_HasTailCalls &&
1013#endif // _TARGET_AMD64_
1014 !IsStructReturnKind(m_ReturnKind);
1015
1016 // All new code is generated for the latest GCINFO_VERSION.
1017 // So, always encode RetunrKind and encode ReversePInvokeFrameSlot where applicable.
1018 if (slimHeader)
1019 {
1020 // Slim encoding means nothing special, partially interruptible, maybe a default frame register
1021 GCINFO_WRITE(m_Info1, 0, 1, FlagsSize); // Slim encoding
1022 GCINFO_WRITE(m_Info1, (m_StackBaseRegister == NO_STACK_BASE_REGISTER) ? 0 : 1, 1, FlagsSize);
1023
1024 GCINFO_WRITE(m_Info1, m_ReturnKind, SIZE_OF_RETURN_KIND_IN_SLIM_HEADER, RetKindSize);
1025 }
1026 else
1027 {
1028 GCINFO_WRITE(m_Info1, 1, 1, FlagsSize); // Fat encoding
1029 GCINFO_WRITE(m_Info1, (m_IsVarArg ? 1 : 0), 1, FlagsSize);
1030 GCINFO_WRITE(m_Info1, (hasSecurityObject ? 1 : 0), 1, FlagsSize);
1031 GCINFO_WRITE(m_Info1, (hasGSCookie ? 1 : 0), 1, FlagsSize);
1032 GCINFO_WRITE(m_Info1, ((m_PSPSymStackSlot != NO_PSP_SYM) ? 1 : 0), 1, FlagsSize);
1033 GCINFO_WRITE(m_Info1, m_contextParamType, 2, FlagsSize);
1034 GCINFO_WRITE(m_Info1, ((m_StackBaseRegister != NO_STACK_BASE_REGISTER) ? 1 : 0), 1, FlagsSize);
1035#ifdef _TARGET_AMD64_
1036 GCINFO_WRITE(m_Info1, (m_WantsReportOnlyLeaf ? 1 : 0), 1, FlagsSize);
1037#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
1038 GCINFO_WRITE(m_Info1, (m_HasTailCalls ? 1 : 0), 1, FlagsSize);
1039#endif // _TARGET_AMD64_
1040 GCINFO_WRITE(m_Info1, ((m_SizeOfEditAndContinuePreservedArea != NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA) ? 1 : 0), 1, FlagsSize);
1041 GCINFO_WRITE(m_Info1, (hasReversePInvokeFrame ? 1 : 0), 1, FlagsSize);
1042
1043 GCINFO_WRITE(m_Info1, m_ReturnKind, SIZE_OF_RETURN_KIND_IN_FAT_HEADER, RetKindSize);
1044 }
1045
1046 _ASSERTE( m_CodeLength > 0 );
1047 _ASSERTE(DENORMALIZE_CODE_LENGTH(NORMALIZE_CODE_LENGTH(m_CodeLength)) == m_CodeLength);
1048 GCINFO_WRITE_VARL_U(m_Info1, NORMALIZE_CODE_LENGTH(m_CodeLength), CODE_LENGTH_ENCBASE, CodeLengthSize);
1049
1050 if(hasGSCookie)
1051 {
1052 _ASSERTE(!slimHeader);
1053 // Save the valid code range, to be used for determining when GS cookie validation
1054 // should be performed
1055 // Encode an intersection of valid offsets
1056 UINT32 intersectionStart = m_GSCookieValidRangeStart;
1057 UINT32 intersectionEnd = m_GSCookieValidRangeEnd;
1058
1059 _ASSERTE(intersectionStart > 0 && intersectionStart < m_CodeLength);
1060 _ASSERTE(intersectionEnd > 0 && intersectionEnd <= m_CodeLength);
1061 _ASSERTE(intersectionStart <= intersectionEnd);
1062 UINT32 normPrologSize = NORMALIZE_CODE_OFFSET(intersectionStart);
1063 UINT32 normEpilogSize = NORMALIZE_CODE_OFFSET(m_CodeLength) - NORMALIZE_CODE_OFFSET(intersectionEnd);
1064 _ASSERTE(normPrologSize > 0 && normPrologSize < m_CodeLength);
1065 _ASSERTE(normEpilogSize < m_CodeLength);
1066
1067 GCINFO_WRITE_VARL_U(m_Info1, normPrologSize-1, NORM_PROLOG_SIZE_ENCBASE, ProEpilogSize);
1068 GCINFO_WRITE_VARL_U(m_Info1, normEpilogSize, NORM_EPILOG_SIZE_ENCBASE, ProEpilogSize);
1069 }
1070 else if (hasSecurityObject || hasContextParamType)
1071 {
1072 _ASSERTE(!slimHeader);
1073 // Save the prolog size, to be used for determining when it is not safe
1074 // to report generics param context and the security object
1075 _ASSERTE(m_GSCookieValidRangeStart > 0 && m_GSCookieValidRangeStart < m_CodeLength);
1076 UINT32 normPrologSize = NORMALIZE_CODE_OFFSET(m_GSCookieValidRangeStart);
1077 _ASSERTE(normPrologSize > 0 && normPrologSize < m_CodeLength);
1078
1079 GCINFO_WRITE_VARL_U(m_Info1, normPrologSize-1, NORM_PROLOG_SIZE_ENCBASE, ProEpilogSize);
1080 }
1081
1082 // Encode the offset to the security object.
1083 if(hasSecurityObject)
1084 {
1085 _ASSERTE(!slimHeader);
1086#ifdef _DEBUG
1087 LOG((LF_GCINFO, LL_INFO1000, "Security object at " FMT_STK "\n",
1088 DBG_STK(m_SecurityObjectStackSlot)
1089 ));
1090#endif
1091
1092 GCINFO_WRITE_VARL_S(m_Info1, NORMALIZE_STACK_SLOT(m_SecurityObjectStackSlot), SECURITY_OBJECT_STACK_SLOT_ENCBASE, SecObjSize);
1093 }
1094
1095 // Encode the offset to the GS cookie.
1096 if(hasGSCookie)
1097 {
1098 _ASSERTE(!slimHeader);
1099#ifdef _DEBUG
1100 LOG((LF_GCINFO, LL_INFO1000, "GS cookie at " FMT_STK "\n",
1101 DBG_STK(m_GSCookieStackSlot)
1102 ));
1103#endif
1104
1105 GCINFO_WRITE_VARL_S(m_Info1, NORMALIZE_STACK_SLOT(m_GSCookieStackSlot), GS_COOKIE_STACK_SLOT_ENCBASE, GsCookieSize);
1106
1107 }
1108
1109 // Encode the offset to the PSPSym.
1110 // The PSPSym is relative to the caller SP on IA64 and the initial stack pointer before stack allocations on X64.
1111 if(m_PSPSymStackSlot != NO_PSP_SYM)
1112 {
1113 _ASSERTE(!slimHeader);
1114#ifdef _DEBUG
1115 LOG((LF_GCINFO, LL_INFO1000, "Parent PSP at " FMT_STK "\n", DBG_STK(m_PSPSymStackSlot)));
1116#endif
1117 GCINFO_WRITE_VARL_S(m_Info1, NORMALIZE_STACK_SLOT(m_PSPSymStackSlot), PSP_SYM_STACK_SLOT_ENCBASE, PspSymSize);
1118 }
1119
1120 // Encode the offset to the generics type context.
1121 if(m_GenericsInstContextStackSlot != NO_GENERICS_INST_CONTEXT)
1122 {
1123 _ASSERTE(!slimHeader);
1124#ifdef _DEBUG
1125 LOG((LF_GCINFO, LL_INFO1000, "Generics instantiation context at " FMT_STK "\n",
1126 DBG_STK(m_GenericsInstContextStackSlot)
1127 ));
1128#endif
1129 GCINFO_WRITE_VARL_S(m_Info1, NORMALIZE_STACK_SLOT(m_GenericsInstContextStackSlot), GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE, GenericsCtxSize);
1130 }
1131
1132 if(!slimHeader && (m_StackBaseRegister != NO_STACK_BASE_REGISTER))
1133 {
1134 GCINFO_WRITE_VARL_U(m_Info1, NORMALIZE_STACK_BASE_REGISTER(m_StackBaseRegister), STACK_BASE_REGISTER_ENCBASE, StackBaseSize);
1135 }
1136
1137 if (m_SizeOfEditAndContinuePreservedArea != NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA)
1138 {
1139 GCINFO_WRITE_VARL_U(m_Info1, m_SizeOfEditAndContinuePreservedArea, SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE, EncPreservedSlots);
1140 }
1141
1142 if (hasReversePInvokeFrame)
1143 {
1144 _ASSERTE(!slimHeader);
1145 GCINFO_WRITE_VARL_S(m_Info1, NORMALIZE_STACK_SLOT(m_ReversePInvokeFrameSlot), REVERSE_PINVOKE_FRAME_ENCBASE, ReversePInvokeFrameSize);
1146 }
1147
1148#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
1149 if (!slimHeader)
1150 {
1151 _ASSERTE( m_SizeOfStackOutgoingAndScratchArea != (UINT32)-1 );
1152 GCINFO_WRITE_VARL_U(m_Info1, NORMALIZE_SIZE_OF_STACK_AREA(m_SizeOfStackOutgoingAndScratchArea), SIZE_OF_STACK_AREA_ENCBASE, FixedAreaSize);
1153 }
1154#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA
1155
1156 UINT32 numInterruptibleRanges = (UINT32) m_InterruptibleRanges.Count();
1157
1158 InterruptibleRange *pRanges = NULL;
1159 if(numInterruptibleRanges)
1160 {
1161 pRanges = (InterruptibleRange*) m_pAllocator->Alloc(numInterruptibleRanges * sizeof(InterruptibleRange));
1162 m_InterruptibleRanges.CopyTo(pRanges);
1163 }
1164
1165 BitArray liveState(m_pAllocator, m_NumSlots);
1166 BitArray couldBeLive(m_pAllocator, m_NumSlots);
1167 liveState.ClearAll();
1168 couldBeLive.ClearAll();
1169
1170#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
1171 _ASSERTE(m_NumCallSites == 0 || m_pCallSites != NULL);
1172
1173 ///////////////////////////////////////////////////////////////////////
1174 // Normalize call sites
1175 // Eliminate call sites that fall inside interruptible ranges
1176 ///////////////////////////////////////////////////////////////////////
1177
1178 UINT32 numCallSites = 0;
1179 for(UINT32 callSiteIndex = 0; callSiteIndex < m_NumCallSites; callSiteIndex++)
1180 {
1181 UINT32 callSite = m_pCallSites[callSiteIndex];
1182 // There's a contract with the EE that says for non-leaf stack frames, where the
1183 // method is stopped at a call site, the EE will not query with the return PC, but
1184 // rather the return PC *minus 1*.
1185 // The reason is that variable/register liveness may change at the instruction immediately after the
1186 // call, so we want such frames to appear as if they are "within" the call.
1187 // Since we use "callSite" as the "key" when we search for the matching descriptor, also subtract 1 here
1188 // (after, of course, adding the size of the call instruction to get the return PC).
1189 callSite += m_pCallSiteSizes[callSiteIndex] - 1;
1190
1191 _ASSERTE(DENORMALIZE_CODE_OFFSET(NORMALIZE_CODE_OFFSET(callSite)) == callSite);
1192 UINT32 normOffset = NORMALIZE_CODE_OFFSET(callSite);
1193
1194 BOOL keepIt = TRUE;
1195
1196 for(UINT32 intRangeIndex = 0; intRangeIndex < numInterruptibleRanges; intRangeIndex++)
1197 {
1198 InterruptibleRange *pRange = &pRanges[intRangeIndex];
1199 if(pRange->NormStopOffset > normOffset)
1200 {
1201 if(pRange->NormStartOffset <= normOffset)
1202 {
1203 keepIt = FALSE;
1204 }
1205 break;
1206 }
1207 }
1208
1209 if(keepIt)
1210 m_pCallSites[numCallSites++] = normOffset;
1211 }
1212
1213 GCINFO_WRITE_VARL_U(m_Info1, NORMALIZE_NUM_SAFE_POINTS(numCallSites), NUM_SAFE_POINTS_ENCBASE, NumCallSitesSize);
1214 m_NumCallSites = numCallSites;
1215#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
1216
1217 if (slimHeader)
1218 {
1219 _ASSERTE(numInterruptibleRanges == 0);
1220 }
1221 else
1222 {
1223 GCINFO_WRITE_VARL_U(m_Info1, NORMALIZE_NUM_INTERRUPTIBLE_RANGES(numInterruptibleRanges), NUM_INTERRUPTIBLE_RANGES_ENCBASE, NumRangesSize);
1224 }
1225
1226
1227
1228#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
1229 ///////////////////////////////////////////////////////////////////////
1230 // Encode call site offsets
1231 ///////////////////////////////////////////////////////////////////////
1232
1233 UINT32 numBitsPerOffset = CeilOfLog2(NORMALIZE_CODE_OFFSET(m_CodeLength));
1234
1235 for(UINT32 callSiteIndex = 0; callSiteIndex < m_NumCallSites; callSiteIndex++)
1236 {
1237 UINT32 normOffset = m_pCallSites[callSiteIndex];
1238
1239 _ASSERTE(normOffset < (UINT32)1 << (numBitsPerOffset+1));
1240 GCINFO_WRITE(m_Info1, normOffset, numBitsPerOffset, CallSitePosSize);
1241 }
1242#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
1243
1244
1245 ///////////////////////////////////////////////////////////////////////
1246 // Encode fully-interruptible ranges
1247 ///////////////////////////////////////////////////////////////////////
1248
1249 if(numInterruptibleRanges)
1250 {
1251 UINT32 lastStopOffset = 0;
1252
1253 for(UINT32 i = 0; i < numInterruptibleRanges; i++)
1254 {
1255 UINT32 normStartOffset = pRanges[i].NormStartOffset;
1256 UINT32 normStopOffset = pRanges[i].NormStopOffset;
1257
1258 size_t normStartDelta = normStartOffset - lastStopOffset;
1259 size_t normStopDelta = normStopOffset - normStartOffset;
1260 _ASSERTE(normStopDelta > 0);
1261
1262 lastStopOffset = normStopOffset;
1263
1264 GCINFO_WRITE_VARL_U(m_Info1, normStartDelta, INTERRUPTIBLE_RANGE_DELTA1_ENCBASE, RangeSize);
1265
1266 GCINFO_WRITE_VARL_U(m_Info1, normStopDelta-1, INTERRUPTIBLE_RANGE_DELTA2_ENCBASE, RangeSize);
1267 }
1268 }
1269
1270
1271 ///////////////////////////////////////////////////////////////////////
1272 // Pre-process transitions
1273 ///////////////////////////////////////////////////////////////////////
1274
1275
1276 size_t numTransitions = m_LifetimeTransitions.Count();
1277 LifetimeTransition *pTransitions = (LifetimeTransition*)m_pAllocator->Alloc(numTransitions * sizeof(LifetimeTransition));
1278 m_LifetimeTransitions.CopyTo(pTransitions);
1279
1280 LifetimeTransition* pEndTransitions = pTransitions + numTransitions;
1281 LifetimeTransition* pCurrent;
1282
1283 //-----------------------------------------------------------------
1284 // Sort the lifetime transitions by offset (then by slot id).
1285 //-----------------------------------------------------------------
1286
1287 // Don't use the CQuickSort algorithm, it's prone to stack overflows
1288 qsort(
1289 pTransitions,
1290 numTransitions,
1291 sizeof(LifetimeTransition),
1292 CompareLifetimeTransitionsByOffsetThenSlot
1293 );
1294
1295 // Eliminate transitions outside the method
1296 while(pEndTransitions > pTransitions)
1297 {
1298 LifetimeTransition *pPrev = pEndTransitions - 1;
1299 if(pPrev->CodeOffset < m_CodeLength)
1300 break;
1301
1302 _ASSERTE(pPrev->CodeOffset == m_CodeLength && !pPrev->BecomesLive);
1303 pEndTransitions = pPrev;
1304 }
1305
1306 // Now eliminate any pairs of dead/live transitions for the same slot at the same offset.
1307 EliminateRedundantLiveDeadPairs(&pTransitions, &numTransitions, &pEndTransitions);
1308
1309#ifdef _DEBUG
1310 numTransitions = -1;
1311#endif
1312 ///////////////////////////////////////////////////////////////////////
1313 // Sort the slot table
1314 ///////////////////////////////////////////////////////////////////////
1315
1316 {
1317 GcSlotDescAndId* sortedSlots = (GcSlotDescAndId*) m_pAllocator->Alloc(m_NumSlots * sizeof(GcSlotDescAndId));
1318 UINT32* sortOrder = (UINT32*) m_pAllocator->Alloc(m_NumSlots * sizeof(UINT32));
1319
1320 for(UINT32 i = 0; i < m_NumSlots; i++)
1321 {
1322 sortedSlots[i].m_SlotDesc = m_SlotTable[i];
1323 sortedSlots[i].m_SlotId = i;
1324 }
1325
1326 qsort(sortedSlots, m_NumSlots, sizeof(GcSlotDescAndId), CompareSlotDescAndIdBySlotDesc);
1327
1328 for(UINT32 i = 0; i < m_NumSlots; i++)
1329 {
1330 sortOrder[sortedSlots[i].m_SlotId] = i;
1331 }
1332
1333 // Re-order the slot table
1334 for(UINT32 i = 0; i < m_NumSlots; i++)
1335 {
1336 m_SlotTable[i] = sortedSlots[i].m_SlotDesc;
1337 }
1338
1339 // Update transitions to assign new slot ids
1340 for(pCurrent = pTransitions; pCurrent < pEndTransitions; pCurrent++)
1341 {
1342 UINT32 newSlotId = sortOrder[pCurrent->SlotId];
1343 pCurrent->SlotId = newSlotId;
1344 }
1345
1346#ifdef MUST_CALL_JITALLOCATOR_FREE
1347 m_pAllocator->Free( sortedSlots );
1348 m_pAllocator->Free( sortOrder );
1349#endif
1350 }
1351
1352#if CODE_OFFSETS_NEED_NORMALIZATION
1353 // Do a pass to normalize transition offsets
1354 for(pCurrent = pTransitions; pCurrent < pEndTransitions; pCurrent++)
1355 {
1356 _ASSERTE(pCurrent->CodeOffset <= m_CodeLength);
1357 pCurrent->CodeOffset = NORMALIZE_CODE_OFFSET(pCurrent->CodeOffset);
1358 }
1359#endif
1360
1361 ///////////////////////////////////////////////////////////////////
1362 // Find out which slots are really used
1363 ///////////////////////////////////////////////////////////////////
1364
1365 couldBeLive.ClearAll();
1366
1367#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
1368 if(m_NumCallSites)
1369 {
1370 _ASSERTE(m_pCallSites != NULL);
1371 liveState.ClearAll();
1372
1373 UINT32 callSiteIndex = 0;
1374 UINT32 callSite = m_pCallSites[0];
1375
1376 for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
1377 {
1378 if(pCurrent->CodeOffset > callSite)
1379 {
1380 couldBeLive |= liveState;
1381
1382 if(++callSiteIndex == m_NumCallSites)
1383 break;
1384
1385 callSite = m_pCallSites[callSiteIndex];
1386 }
1387 else
1388 {
1389 UINT32 slotIndex = pCurrent->SlotId;
1390 if(!IsAlwaysScratch(m_SlotTable[slotIndex]))
1391 {
1392 BYTE becomesLive = pCurrent->BecomesLive;
1393 _ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
1394 || (!liveState.ReadBit(slotIndex) && becomesLive));
1395
1396 liveState.WriteBit(slotIndex, becomesLive);
1397 }
1398 pCurrent++;
1399 }
1400 }
1401 // There could be call sites after the last transition
1402 if(callSiteIndex < m_NumCallSites)
1403 {
1404 couldBeLive |= liveState;
1405 }
1406 }
1407
1408 if(numInterruptibleRanges)
1409 {
1410 liveState.ClearAll();
1411
1412 InterruptibleRange *pCurrentRange = pRanges;
1413 InterruptibleRange *pEndRanges = pRanges + numInterruptibleRanges;
1414
1415 for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
1416 {
1417 // Find the first transition at offset > of the start of the current range
1418 LifetimeTransition *pFirstAfterStart = pCurrent;
1419 while(pFirstAfterStart->CodeOffset <= pCurrentRange->NormStartOffset)
1420 {
1421 UINT32 slotIndex = (UINT32) (pFirstAfterStart->SlotId);
1422 BYTE becomesLive = pFirstAfterStart->BecomesLive;
1423 _ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
1424 || (!liveState.ReadBit(slotIndex) && becomesLive));
1425 liveState.WriteBit(slotIndex, becomesLive);
1426
1427 if(++pFirstAfterStart == pEndTransitions)
1428 break;
1429 }
1430
1431 couldBeLive |= liveState;
1432
1433 // Now iterate through all the remaining transitions in the range,
1434 // making the offset range-relative, and tracking live state
1435 UINT32 rangeStop = pCurrentRange->NormStopOffset;
1436
1437 for(pCurrent = pFirstAfterStart; pCurrent < pEndTransitions && pCurrent->CodeOffset < rangeStop; pCurrent++)
1438 {
1439 UINT32 slotIndex = (UINT32) (pCurrent->SlotId);
1440 BYTE becomesLive = pCurrent->BecomesLive;
1441 _ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
1442 || (!liveState.ReadBit(slotIndex) && becomesLive));
1443 liveState.WriteBit(slotIndex, becomesLive);
1444 couldBeLive.SetBit(slotIndex);
1445 }
1446
1447 // Move to the next range
1448 if(pCurrentRange < pEndRanges - 1)
1449 {
1450 pCurrentRange++;
1451 }
1452 else
1453 {
1454 break;
1455 }
1456 }
1457 }
1458
1459 //-----------------------------------------------------------------
1460 // Mark unneeded slots as deleted
1461 //-----------------------------------------------------------------
1462
1463 UINT32 numUsedSlots = 0;
1464 for(UINT32 i = 0; i < m_NumSlots; i++)
1465 {
1466 if(!(m_SlotTable[i].IsUntracked()) && (couldBeLive.ReadBit(i) == 0))
1467 {
1468 m_SlotTable[i].MarkDeleted();
1469 }
1470 else
1471 numUsedSlots++;
1472 }
1473
1474 if(numUsedSlots < m_NumSlots)
1475 {
1476 // Delete transitions on unused slots
1477 LifetimeTransition *pNextFree = pTransitions;
1478 for(pCurrent = pTransitions; pCurrent < pEndTransitions; pCurrent++)
1479 {
1480 UINT32 slotId = pCurrent->SlotId;
1481 if(!m_SlotTable[slotId].IsDeleted())
1482 {
1483 if(pCurrent > pNextFree)
1484 {
1485 *pNextFree = *pCurrent;
1486 }
1487 pNextFree++;
1488 }
1489 }
1490 pEndTransitions = pNextFree;
1491 }
1492
1493#else // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
1494
1495 UINT32 numUsedSlots = m_NumSlots;
1496
1497#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
1498
1499
1500 ///////////////////////////////////////////////////////////////////////
1501 // Encode slot table
1502 ///////////////////////////////////////////////////////////////////////
1503
1504 //------------------------------------------------------------------
1505 // Count registers and stack slots
1506 //------------------------------------------------------------------
1507
1508 UINT32 numRegisters;
1509 UINT32 numUntrackedSlots;
1510 UINT32 numStackSlots;
1511
1512 {
1513 UINT32 numDeleted = 0;
1514 UINT32 i;
1515 for(i = 0; i < m_NumSlots && m_SlotTable[i].IsRegister(); i++)
1516 {
1517 if(m_SlotTable[i].IsDeleted())
1518 {
1519 numDeleted++;
1520 }
1521 }
1522 numRegisters = i - numDeleted;
1523
1524 for(; i < m_NumSlots && !m_SlotTable[i].IsUntracked(); i++)
1525 {
1526 if(m_SlotTable[i].IsDeleted())
1527 {
1528 numDeleted++;
1529 }
1530 }
1531 numStackSlots = i - (numRegisters + numDeleted);
1532 }
1533 numUntrackedSlots = numUsedSlots - (numRegisters + numStackSlots);
1534
1535 // Common case: nothing, or a few registers
1536 if (numRegisters)
1537 {
1538 GCINFO_WRITE(m_Info1, 1, 1, FlagsSize);
1539 GCINFO_WRITE_VARL_U(m_Info1, numRegisters, NUM_REGISTERS_ENCBASE, NumRegsSize);
1540 }
1541 else
1542 {
1543 GCINFO_WRITE(m_Info1, 0, 1, FlagsSize);
1544 }
1545 if (numStackSlots || numUntrackedSlots)
1546 {
1547 GCINFO_WRITE(m_Info1, 1, 1, FlagsSize);
1548 GCINFO_WRITE_VARL_U(m_Info1, numStackSlots, NUM_STACK_SLOTS_ENCBASE, NumStackSize);
1549 GCINFO_WRITE_VARL_U(m_Info1, numUntrackedSlots, NUM_UNTRACKED_SLOTS_ENCBASE, NumUntrackedSize);
1550 }
1551 else
1552 {
1553 GCINFO_WRITE(m_Info1, 0, 1, FlagsSize);
1554 }
1555
1556 UINT32 currentSlot = 0;
1557
1558 if(numUsedSlots == 0)
1559 goto lExitSuccess;
1560
1561 if(numRegisters > 0)
1562 {
1563 GcSlotDesc *pSlotDesc;
1564 do
1565 {
1566 _ASSERTE(currentSlot < m_NumSlots);
1567 pSlotDesc = &m_SlotTable[currentSlot++];
1568 }
1569 while(pSlotDesc->IsDeleted());
1570 _ASSERTE(pSlotDesc->IsRegister());
1571
1572 // Encode slot identification
1573 UINT32 currentNormRegNum = NORMALIZE_REGISTER(pSlotDesc->Slot.RegisterNumber);
1574 GCINFO_WRITE_VARL_U(m_Info1, currentNormRegNum, REGISTER_ENCBASE, RegSlotSize);
1575 GCINFO_WRITE(m_Info1, pSlotDesc->Flags, 2, RegSlotSize);
1576
1577 for(UINT32 j = 1; j < numRegisters; j++)
1578 {
1579 UINT32 lastNormRegNum = currentNormRegNum;
1580 GcSlotFlags lastFlags = pSlotDesc->Flags;
1581
1582 do
1583 {
1584 _ASSERTE(currentSlot < m_NumSlots);
1585 pSlotDesc = &m_SlotTable[currentSlot++];
1586 }
1587 while(pSlotDesc->IsDeleted());
1588 _ASSERTE(pSlotDesc->IsRegister());
1589
1590 currentNormRegNum = NORMALIZE_REGISTER(pSlotDesc->Slot.RegisterNumber);
1591
1592 if(lastFlags != GC_SLOT_IS_REGISTER)
1593 {
1594 GCINFO_WRITE_VARL_U(m_Info1, currentNormRegNum, REGISTER_ENCBASE, RegSlotSize);
1595 GCINFO_WRITE(m_Info1, pSlotDesc->Flags, 2, RegSlotSize);
1596 }
1597 else
1598 {
1599 _ASSERTE(pSlotDesc->Flags == GC_SLOT_IS_REGISTER);
1600 GCINFO_WRITE_VARL_U(m_Info1, currentNormRegNum - lastNormRegNum - 1, REGISTER_DELTA_ENCBASE, RegSlotSize);
1601 }
1602 }
1603 }
1604
1605 if(numStackSlots > 0)
1606 {
1607 GcSlotDesc *pSlotDesc;
1608 do
1609 {
1610 _ASSERTE(currentSlot < m_NumSlots);
1611 pSlotDesc = &m_SlotTable[currentSlot++];
1612 }
1613 while(pSlotDesc->IsDeleted());
1614 _ASSERTE(!pSlotDesc->IsRegister());
1615 _ASSERTE(!pSlotDesc->IsUntracked());
1616
1617 // Encode slot identification
1618 _ASSERTE((pSlotDesc->Slot.Stack.Base & ~3) == 0);
1619 GCINFO_WRITE(m_Info1, pSlotDesc->Slot.Stack.Base, 2, StackSlotSize);
1620 INT32 currentNormStackSlot = NORMALIZE_STACK_SLOT(pSlotDesc->Slot.Stack.SpOffset);
1621 GCINFO_WRITE_VARL_S(m_Info1, currentNormStackSlot, STACK_SLOT_ENCBASE, StackSlotSize);
1622
1623 GCINFO_WRITE(m_Info1, pSlotDesc->Flags, 2, StackSlotSize);
1624
1625 for(UINT32 j = 1; j < numStackSlots; j++)
1626 {
1627 INT32 lastNormStackSlot = currentNormStackSlot;
1628 GcSlotFlags lastFlags = pSlotDesc->Flags;
1629
1630 do
1631 {
1632 _ASSERTE(currentSlot < m_NumSlots);
1633 pSlotDesc = &m_SlotTable[currentSlot++];
1634 }
1635 while(pSlotDesc->IsDeleted());
1636 _ASSERTE(!pSlotDesc->IsRegister());
1637 _ASSERTE(!pSlotDesc->IsUntracked());
1638
1639 currentNormStackSlot = NORMALIZE_STACK_SLOT(pSlotDesc->Slot.Stack.SpOffset);
1640
1641 _ASSERTE((pSlotDesc->Slot.Stack.Base & ~3) == 0);
1642 GCINFO_WRITE(m_Info1, pSlotDesc->Slot.Stack.Base, 2, StackSlotSize);
1643
1644 if(lastFlags != GC_SLOT_BASE)
1645 {
1646 GCINFO_WRITE_VARL_S(m_Info1, currentNormStackSlot, STACK_SLOT_ENCBASE, StackSlotSize);
1647 GCINFO_WRITE(m_Info1, pSlotDesc->Flags, 2, StackSlotSize);
1648 }
1649 else
1650 {
1651 _ASSERTE(pSlotDesc->Flags == GC_SLOT_BASE);
1652 GCINFO_WRITE_VARL_U(m_Info1, currentNormStackSlot - lastNormStackSlot, STACK_SLOT_DELTA_ENCBASE, StackSlotSize);
1653 }
1654 }
1655 }
1656
1657 if(numUntrackedSlots > 0)
1658 {
1659 GcSlotDesc *pSlotDesc;
1660 do
1661 {
1662 _ASSERTE(currentSlot < m_NumSlots);
1663 pSlotDesc = &m_SlotTable[currentSlot++];
1664 }
1665 while(pSlotDesc->IsDeleted());
1666 _ASSERTE(!pSlotDesc->IsRegister());
1667 _ASSERTE(pSlotDesc->IsUntracked());
1668
1669 // Encode slot identification
1670 _ASSERTE((pSlotDesc->Slot.Stack.Base & ~3) == 0);
1671 GCINFO_WRITE(m_Info1, pSlotDesc->Slot.Stack.Base, 2, UntrackedSlotSize);
1672 INT32 currentNormStackSlot = NORMALIZE_STACK_SLOT(pSlotDesc->Slot.Stack.SpOffset);
1673 GCINFO_WRITE_VARL_S(m_Info1, currentNormStackSlot, STACK_SLOT_ENCBASE, UntrackedSlotSize);
1674
1675 GCINFO_WRITE(m_Info1, pSlotDesc->Flags, 2, UntrackedSlotSize);
1676
1677 for(UINT32 j = 1; j < numUntrackedSlots; j++)
1678 {
1679 INT32 lastNormStackSlot = currentNormStackSlot;
1680 GcSlotFlags lastFlags = pSlotDesc->Flags;
1681
1682 do
1683 {
1684 _ASSERTE(currentSlot < m_NumSlots);
1685 pSlotDesc = &m_SlotTable[currentSlot++];
1686 }
1687 while(pSlotDesc->IsDeleted());
1688 _ASSERTE(!pSlotDesc->IsRegister());
1689 _ASSERTE(pSlotDesc->IsUntracked());
1690
1691 currentNormStackSlot = NORMALIZE_STACK_SLOT(pSlotDesc->Slot.Stack.SpOffset);
1692
1693 _ASSERTE((pSlotDesc->Slot.Stack.Base & ~3) == 0);
1694 GCINFO_WRITE(m_Info1, pSlotDesc->Slot.Stack.Base, 2, UntrackedSlotSize);
1695
1696 if(lastFlags != GC_SLOT_UNTRACKED)
1697 {
1698 GCINFO_WRITE_VARL_S(m_Info1, currentNormStackSlot, STACK_SLOT_ENCBASE, UntrackedSlotSize);
1699 GCINFO_WRITE(m_Info1, pSlotDesc->Flags, 2, UntrackedSlotSize);
1700 }
1701 else
1702 {
1703 _ASSERTE(pSlotDesc->Flags == GC_SLOT_UNTRACKED);
1704 GCINFO_WRITE_VARL_U(m_Info1, currentNormStackSlot - lastNormStackSlot, STACK_SLOT_DELTA_ENCBASE, UntrackedSlotSize);
1705 }
1706 }
1707 }
1708
1709
1710#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
1711 //-----------------------------------------------------------------
1712 // Encode GC info at call sites
1713 //-----------------------------------------------------------------
1714
1715 if(m_NumCallSites)
1716 {
1717
1718 _ASSERTE(m_pCallSites != NULL);
1719
1720 liveState.ClearAll();
1721
1722 UINT32 callSiteIndex = 0;
1723 UINT32 callSite = m_pCallSites[0];
1724
1725 // Create a hash table for storing the locations of the live sets
1726 LiveStateHashTable hashMap(m_pAllocator);
1727
1728 bool outOfMemory = false;
1729 try
1730 {
1731 for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
1732 {
1733 if(pCurrent->CodeOffset > callSite)
1734 {
1735 // Time to record the call site
1736
1737 // Add it to the table if it doesn't exist
1738 UINT32 liveStateOffset = 0;
1739 if (!hashMap.Lookup(&liveState, &liveStateOffset))
1740 {
1741 BitArray * newLiveState = new (m_pAllocator) BitArray(m_pAllocator, m_NumSlots);
1742 *newLiveState = liveState;
1743 hashMap.Set(newLiveState, (UINT32)(-1));
1744 }
1745
1746
1747 if(++callSiteIndex == m_NumCallSites)
1748 break;
1749
1750 callSite = m_pCallSites[callSiteIndex];
1751 }
1752 else
1753 {
1754 UINT32 slotIndex = pCurrent->SlotId;
1755 BYTE becomesLive = pCurrent->BecomesLive;
1756 _ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
1757 || (!liveState.ReadBit(slotIndex) && becomesLive));
1758 liveState.WriteBit(slotIndex, becomesLive);
1759 pCurrent++;
1760 }
1761 }
1762
1763 // Check for call sites at offsets past the last transition
1764 if (callSiteIndex < m_NumCallSites)
1765 {
1766 UINT32 liveStateOffset = 0;
1767 if (!hashMap.Lookup(&liveState, &liveStateOffset))
1768 {
1769 BitArray * newLiveState = new (m_pAllocator) BitArray(m_pAllocator, m_NumSlots);
1770 *newLiveState = liveState;
1771 hashMap.Set(newLiveState, (UINT32)(-1));
1772 }
1773 }
1774 }
1775 catch (GcInfoNoMemoryException&)
1776 {
1777 outOfMemory = true;
1778 }
1779
1780 if (outOfMemory)
1781 {
1782 m_pNoMem();
1783 }
1784
1785 // Figure out the largest offset, and total size of the sets
1786 // Be sure to figure out the largest offset in the order that we will be emitting
1787 // them in and not the order of their appearances in the safe point array.
1788 // TODO: we should sort this to improve locality (the more frequent ones at the beginning)
1789 // and to improve the indirection size (if the largest one is last, we *might* be able
1790 // so use 1 less bit for each indirection for the offset encoding).
1791 UINT32 largestSetOffset = 0;
1792 UINT32 sizeofSets = 0;
1793 for (LiveStateHashTable::KeyIterator iter = hashMap.Begin(), end = hashMap.End(); !iter.Equal(end); iter.Next())
1794 {
1795 largestSetOffset = sizeofSets;
1796 sizeofSets += SizeofSlotStateVarLengthVector(*iter.Get(), LIVESTATE_RLE_SKIP_ENCBASE, LIVESTATE_RLE_RUN_ENCBASE);
1797 }
1798
1799 // Now that we know the largest offset, we can figure out how much the indirection
1800 // will cost us and commit
1801 UINT32 numBitsPerPointer = ((largestSetOffset < 2) ? 1 : CeilOfLog2(largestSetOffset + 1));
1802 const size_t sizeofEncodedNumBitsPerPointer = BitStreamWriter::SizeofVarLengthUnsigned(numBitsPerPointer, POINTER_SIZE_ENCBASE);
1803 const size_t sizeofNoIndirection = m_NumCallSites * (numRegisters + numStackSlots);
1804 const size_t sizeofIndirection = sizeofEncodedNumBitsPerPointer // Encode the pointer sizes
1805 + (m_NumCallSites * numBitsPerPointer) // Encoe the pointers
1806 + 7 // Up to 7 bits of alignment padding
1807 + sizeofSets; // Encode the actual live sets
1808
1809 liveState.ClearAll();
1810
1811 callSiteIndex = 0;
1812 callSite = m_pCallSites[0];
1813
1814 if (sizeofIndirection < sizeofNoIndirection)
1815 {
1816 // we are using an indirection
1817 GCINFO_WRITE(m_Info1, 1, 1, FlagsSize);
1818 GCINFO_WRITE_VARL_U(m_Info1, numBitsPerPointer - 1, POINTER_SIZE_ENCBASE, CallSiteStateSize);
1819
1820 // Now encode the live sets and record the real offset
1821 for (LiveStateHashTable::KeyIterator iter = hashMap.Begin(), end = hashMap.End(); !iter.Equal(end); iter.Next())
1822 {
1823 _ASSERTE(FitsIn<UINT32>(m_Info2.GetBitCount()));
1824 iter.SetValue((UINT32)m_Info2.GetBitCount());
1825 GCINFO_WRITE_VAR_VECTOR(m_Info2, *iter.Get(), LIVESTATE_RLE_SKIP_ENCBASE, LIVESTATE_RLE_RUN_ENCBASE, CallSiteStateSize);
1826 }
1827
1828 _ASSERTE(sizeofSets == m_Info2.GetBitCount());
1829
1830 for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
1831 {
1832 if(pCurrent->CodeOffset > callSite)
1833 {
1834 // Time to encode the call site
1835
1836 // Find the match and emit it
1837 UINT32 liveStateOffset;
1838 bool found = hashMap.Lookup(&liveState, &liveStateOffset);
1839 _ASSERTE(found);
1840 (void)found;
1841 GCINFO_WRITE(m_Info1, liveStateOffset, numBitsPerPointer, CallSiteStateSize);
1842
1843
1844 if(++callSiteIndex == m_NumCallSites)
1845 break;
1846
1847 callSite = m_pCallSites[callSiteIndex];
1848 }
1849 else
1850 {
1851 UINT32 slotIndex = pCurrent->SlotId;
1852 BYTE becomesLive = pCurrent->BecomesLive;
1853 _ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
1854 || (!liveState.ReadBit(slotIndex) && becomesLive));
1855 liveState.WriteBit(slotIndex, becomesLive);
1856 pCurrent++;
1857 }
1858 }
1859
1860 // Encode call sites at offsets past the last transition
1861 {
1862 UINT32 liveStateOffset;
1863 bool found = hashMap.Lookup(&liveState, &liveStateOffset);
1864 _ASSERTE(found);
1865 (void)found;
1866 for( ; callSiteIndex < m_NumCallSites; callSiteIndex++)
1867 {
1868 GCINFO_WRITE(m_Info1, liveStateOffset, numBitsPerPointer, CallSiteStateSize);
1869 }
1870 }
1871 }
1872 else
1873 {
1874 // we are not using an indirection
1875 GCINFO_WRITE(m_Info1, 0, 1, FlagsSize);
1876
1877 for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
1878 {
1879 if(pCurrent->CodeOffset > callSite)
1880 {
1881 // Time to encode the call site
1882 GCINFO_WRITE_VECTOR(m_Info1, liveState, CallSiteStateSize);
1883
1884 if(++callSiteIndex == m_NumCallSites)
1885 break;
1886
1887 callSite = m_pCallSites[callSiteIndex];
1888 }
1889 else
1890 {
1891 UINT32 slotIndex = pCurrent->SlotId;
1892 BYTE becomesLive = pCurrent->BecomesLive;
1893 _ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
1894 || (!liveState.ReadBit(slotIndex) && becomesLive));
1895 liveState.WriteBit(slotIndex, becomesLive);
1896 pCurrent++;
1897 }
1898 }
1899
1900 // Encode call sites at offsets past the last transition
1901 for( ; callSiteIndex < m_NumCallSites; callSiteIndex++)
1902 {
1903 GCINFO_WRITE_VECTOR(m_Info1, liveState, CallSiteStateSize);
1904 }
1905 }
1906
1907#ifdef MUST_CALL_JITALLOCATOR_FREE
1908 // Cleanup
1909 for (LiveStateHashTable::KeyIterator iter = hashMap.Begin(), end = hashMap.End(); !iter.Equal(end); iter.Next())
1910 {
1911 m_pAllocator->Free((LPVOID)iter.Get());
1912 }
1913#endif // MUST_CALL_JITALLOCATOR_FREE
1914
1915 }
1916#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
1917
1918
1919 ///////////////////////////////////////////////////////////////////////
1920 // Fully-interruptible: Encode lifetime transitions
1921 ///////////////////////////////////////////////////////////////////////
1922
1923 if(numInterruptibleRanges)
1924 {
1925#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
1926 //-----------------------------------------------------
1927 // Under partially-interruptible, make the transition
1928 // offsets relative to the interruptible regions
1929 //-----------------------------------------------------
1930
1931 // Compute total length of interruptible ranges
1932 UINT32 totalInterruptibleLength = 0;
1933 for(UINT32 i = 0; i < numInterruptibleRanges; i++)
1934 {
1935 InterruptibleRange *pRange = &pRanges[i];
1936 totalInterruptibleLength += pRange->NormStopOffset - pRange->NormStartOffset;
1937 }
1938 _ASSERTE(totalInterruptibleLength <= NORMALIZE_CODE_OFFSET(m_CodeLength));
1939
1940 liveState.ClearAll();
1941 // Re-use couldBeLive
1942 BitArray& liveStateAtPrevRange = couldBeLive;
1943 liveStateAtPrevRange.ClearAll();
1944
1945 InterruptibleRange *pCurrentRange = pRanges;
1946 InterruptibleRange *pEndRanges = pRanges + numInterruptibleRanges;
1947 UINT32 cumInterruptibleLength = 0;
1948
1949 for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
1950 {
1951 _ASSERTE(!m_SlotTable[pCurrent->SlotId].IsDeleted());
1952
1953 // Find the first transition at offset > of the start of the current range
1954 LifetimeTransition *pFirstAfterStart = pCurrent;
1955 while(pFirstAfterStart->CodeOffset <= pCurrentRange->NormStartOffset)
1956 {
1957 UINT32 slotIndex = (UINT32) (pFirstAfterStart->SlotId);
1958 BYTE becomesLive = pFirstAfterStart->BecomesLive;
1959 _ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
1960 || (!liveState.ReadBit(slotIndex) && becomesLive));
1961 liveState.WriteBit(slotIndex, becomesLive);
1962
1963 if(++pFirstAfterStart == pEndTransitions)
1964 break;
1965 }
1966
1967 // Now compare the liveState with liveStateAtPrevRange
1968 LifetimeTransition *pFirstPreserved = pFirstAfterStart;
1969 for(UINT32 slotIndex = 0; slotIndex < m_NumSlots; slotIndex++)
1970 {
1971 uint32_t isLive = liveState.ReadBit(slotIndex);
1972 if(isLive != liveStateAtPrevRange.ReadBit(slotIndex))
1973 {
1974 pFirstPreserved--;
1975 _ASSERTE(pFirstPreserved >= pCurrent);
1976 pFirstPreserved->CodeOffset = cumInterruptibleLength;
1977 pFirstPreserved->SlotId = slotIndex;
1978 pFirstPreserved->BecomesLive = (isLive) ? 1 : 0;
1979 _ASSERTE(!pFirstPreserved->IsDeleted);
1980 }
1981 }
1982
1983 // Mark all the other transitions since last range as deleted
1984 _ASSERTE(pCurrent <= pFirstPreserved);
1985 while(pCurrent < pFirstPreserved)
1986 {
1987 (pCurrent++)->IsDeleted = TRUE;
1988 }
1989
1990 // Now iterate through all the remaining transitions in the range,
1991 // making the offset range-relative, and tracking live state
1992 UINT32 rangeStop = pCurrentRange->NormStopOffset;
1993
1994 for(pCurrent = pFirstAfterStart; pCurrent < pEndTransitions && pCurrent->CodeOffset < rangeStop; pCurrent++)
1995 {
1996 pCurrent->CodeOffset =
1997 pCurrent->CodeOffset -
1998 pCurrentRange->NormStartOffset +
1999 cumInterruptibleLength;
2000
2001 UINT32 slotIndex = (UINT32) (pCurrent->SlotId);
2002 BYTE becomesLive = pCurrent->BecomesLive;
2003 _ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
2004 || (!liveState.ReadBit(slotIndex) && becomesLive));
2005 liveState.WriteBit(slotIndex, becomesLive);
2006 }
2007
2008 // Move to the next range
2009 if(pCurrentRange < pEndRanges - 1)
2010 {
2011 cumInterruptibleLength += pCurrentRange->NormStopOffset - pCurrentRange->NormStartOffset;
2012 pCurrentRange++;
2013
2014 liveStateAtPrevRange = liveState;
2015 }
2016 else
2017 {
2018 pEndTransitions = pCurrent;
2019 break;
2020 }
2021 }
2022
2023 // Make another pass, deleting everything that's marked as deleted
2024 LifetimeTransition *pNextFree = pTransitions;
2025 for(pCurrent = pTransitions; pCurrent < pEndTransitions; pCurrent++)
2026 {
2027 if(!pCurrent->IsDeleted)
2028 {
2029 if(pCurrent > pNextFree)
2030 {
2031 *pNextFree = *pCurrent;
2032 }
2033 pNextFree++;
2034 }
2035 }
2036 pEndTransitions = pNextFree;
2037
2038#else
2039 UINT32 totalInterruptibleLength = NORMALIZE_CODE_OFFSET(m_CodeLength);
2040
2041#endif //PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
2042
2043 //
2044 // Initialize chunk pointers
2045 //
2046 UINT32 numChunks = (totalInterruptibleLength + NUM_NORM_CODE_OFFSETS_PER_CHUNK - 1) / NUM_NORM_CODE_OFFSETS_PER_CHUNK;
2047 _ASSERTE(numChunks > 0);
2048
2049 size_t* pChunkPointers = (size_t*) m_pAllocator->Alloc(numChunks*sizeof(size_t));
2050 ZeroMemory(pChunkPointers, numChunks*sizeof(size_t));
2051
2052 //------------------------------------------------------------------
2053 // Encode transitions
2054 //------------------------------------------------------------------
2055
2056 LOG((LF_GCINFO, LL_INFO1000, "Encoding %i lifetime transitions.\n", pEndTransitions - pTransitions));
2057
2058
2059 liveState.ClearAll();
2060 couldBeLive.ClearAll();
2061
2062 for(pCurrent = pTransitions; pCurrent < pEndTransitions; )
2063 {
2064 _ASSERTE(pCurrent->CodeOffset < m_CodeLength);
2065
2066 UINT32 currentChunk = GetNormCodeOffsetChunk(pCurrent->CodeOffset);
2067 _ASSERTE(currentChunk < numChunks);
2068 UINT32 numTransitionsInCurrentChunk = 1;
2069
2070 for(;;)
2071 {
2072 UINT32 slotIndex = (UINT32) (pCurrent->SlotId);
2073 BYTE becomesLive = pCurrent->BecomesLive;
2074 _ASSERTE((liveState.ReadBit(slotIndex) && !becomesLive)
2075 || (!liveState.ReadBit(slotIndex) && becomesLive));
2076 liveState.WriteBit(slotIndex, becomesLive);
2077 couldBeLive.SetBit(slotIndex);
2078
2079 pCurrent++;
2080 if(pCurrent == pEndTransitions || GetNormCodeOffsetChunk(pCurrent->CodeOffset) != currentChunk)
2081 break;
2082
2083 numTransitionsInCurrentChunk++;
2084 }
2085
2086 //-----------------------------------------------------
2087 // Time to encode the chunk
2088 //-----------------------------------------------------
2089
2090 _ASSERTE(numTransitionsInCurrentChunk > 0);
2091
2092 // Sort the transitions in this chunk by slot
2093 qsort(
2094 pCurrent - numTransitionsInCurrentChunk,
2095 numTransitionsInCurrentChunk,
2096 sizeof(LifetimeTransition),
2097 CompareLifetimeTransitionsBySlot
2098 );
2099
2100 // Save chunk pointer
2101 pChunkPointers[currentChunk] = m_Info2.GetBitCount() + 1;
2102
2103 // Write couldBeLive slot map
2104 GCINFO_WRITE_VAR_VECTOR(m_Info2, couldBeLive, LIVESTATE_RLE_SKIP_ENCBASE, LIVESTATE_RLE_RUN_ENCBASE, ChunkMaskSize);
2105
2106 LOG((LF_GCINFO, LL_INFO100000,
2107 "Chunk %d couldBeLive (%04x-%04x):\n", currentChunk,
2108 currentChunk * NUM_NORM_CODE_OFFSETS_PER_CHUNK,
2109 ((currentChunk + 1) * NUM_NORM_CODE_OFFSETS_PER_CHUNK) - 1
2110 ));
2111
2112 // Write final state
2113 // For all the bits set in couldBeLive.
2114 UINT32 i;
2115 for (BitArrayIterator iter(&couldBeLive); !iter.end(); iter++)
2116 {
2117 i = *iter;
2118 {
2119 _ASSERTE(!m_SlotTable[i].IsDeleted());
2120 _ASSERTE(!m_SlotTable[i].IsUntracked());
2121 GCINFO_WRITE( m_Info2,
2122 liveState.ReadBit(i) ? 1 : 0,
2123 1,
2124 ChunkFinalStateSize
2125 );
2126
2127 LOG((LF_GCINFO, LL_INFO100000,
2128 "\t" LOG_GCSLOTDESC_FMT " %s at end of chunk.\n",
2129 LOG_GCSLOTDESC_ARGS(&m_SlotTable[i]),
2130 liveState.ReadBit(i) ? "live" : "dead"));
2131 }
2132 }
2133
2134 // Write transitions offsets
2135 UINT32 normChunkBaseCodeOffset = currentChunk * NUM_NORM_CODE_OFFSETS_PER_CHUNK;
2136
2137 LifetimeTransition* pT = pCurrent - numTransitionsInCurrentChunk;
2138
2139 for (BitArrayIterator iter(&couldBeLive); !iter.end(); iter++)
2140 {
2141 i = *iter;
2142
2143 while(pT < pCurrent)
2144 {
2145 GcSlotId slotId = pT->SlotId;
2146 if(slotId != i)
2147 break;
2148
2149 _ASSERTE(couldBeLive.ReadBit(slotId));
2150
2151 LOG((LF_GCINFO, LL_INFO100000,
2152 "\tTransition " LOG_GCSLOTDESC_FMT " going %s at offset %04x.\n",
2153 LOG_GCSLOTDESC_ARGS(&m_SlotTable[pT->SlotId]),
2154 pT->BecomesLive ? "live" : "dead",
2155 (int) pT->CodeOffset ));
2156
2157 // Write code offset delta
2158 UINT32 normCodeOffset = pT->CodeOffset;
2159 UINT32 normCodeOffsetDelta = normCodeOffset - normChunkBaseCodeOffset;
2160
2161 // Don't encode transitions at offset 0 as they are useless
2162 if(normCodeOffsetDelta)
2163 {
2164 _ASSERTE(normCodeOffsetDelta < NUM_NORM_CODE_OFFSETS_PER_CHUNK);
2165
2166 GCINFO_WRITE(m_Info2, 1, 1, ChunkTransitionSize);
2167 GCINFO_WRITE(m_Info2, normCodeOffsetDelta, NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2, ChunkTransitionSize);
2168
2169#ifdef MEASURE_GCINFO
2170 m_CurrentMethodSize.NumTransitions++;
2171#endif
2172 }
2173
2174 pT++;
2175 }
2176
2177 // Write terminator
2178 GCINFO_WRITE(m_Info2, 0, 1, ChunkTransitionSize);
2179
2180 }
2181 _ASSERTE(pT == pCurrent);
2182
2183 couldBeLive = liveState;
2184 }
2185
2186 //---------------------------------------------------------------------
2187 // The size of chunk encodings is now known. Write the chunk pointers.
2188 //---------------------------------------------------------------------
2189
2190
2191 // Find the largest pointer
2192 size_t largestPointer = 0;
2193 for(int i = numChunks - 1; i >=0; i--)
2194 {
2195 largestPointer = pChunkPointers[i];
2196 if(largestPointer > 0)
2197 break;
2198 }
2199
2200 UINT32 numBitsPerPointer = CeilOfLog2(largestPointer + 1);
2201 GCINFO_WRITE_VARL_U(m_Info1, numBitsPerPointer, POINTER_SIZE_ENCBASE, ChunkPtrSize);
2202
2203 if(numBitsPerPointer)
2204 {
2205 for(UINT32 i = 0; i < numChunks; i++)
2206 {
2207 GCINFO_WRITE(m_Info1, pChunkPointers[i], numBitsPerPointer, ChunkPtrSize);
2208 }
2209 }
2210
2211 //-------------------------------------------------------------------
2212 // Cleanup
2213 //-------------------------------------------------------------------
2214
2215#ifdef MUST_CALL_JITALLOCATOR_FREE
2216 m_pAllocator->Free(pRanges);
2217 m_pAllocator->Free(pChunkPointers);
2218#endif
2219 }
2220
2221
2222#ifdef MUST_CALL_JITALLOCATOR_FREE
2223 m_pAllocator->Free(pTransitions);
2224#endif
2225
2226
2227lExitSuccess:;
2228
2229 //-------------------------------------------------------------------
2230 // Update global stats
2231 //-------------------------------------------------------------------
2232
2233#ifdef MEASURE_GCINFO
2234 if (slimHeader)
2235 {
2236 g_NumSlimHeaders++;
2237 }
2238 else
2239 {
2240 g_NumFatHeaders++;
2241 }
2242
2243 m_CurrentMethodSize.NumMethods = 1;
2244#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
2245 m_CurrentMethodSize.NumCallSites = m_NumCallSites;
2246#endif
2247 m_CurrentMethodSize.NumRanges = numInterruptibleRanges;
2248 m_CurrentMethodSize.NumRegs = numRegisters;
2249 m_CurrentMethodSize.NumStack = numStackSlots;
2250 m_CurrentMethodSize.NumUntracked = numUntrackedSlots;
2251 m_CurrentMethodSize.SizeOfCode = m_CodeLength;
2252 if(numInterruptibleRanges)
2253 {
2254 g_FiGcInfoSize += m_CurrentMethodSize;
2255 m_CurrentMethodSize.Log(LL_INFO100, "=== FullyInterruptible method breakdown ===\r\n");
2256 g_FiGcInfoSize.Log(LL_INFO10, "=== FullyInterruptible global breakdown ===\r\n");
2257 }
2258 else
2259 {
2260 g_PiGcInfoSize += m_CurrentMethodSize;
2261 m_CurrentMethodSize.Log(LL_INFO100, "=== PartiallyInterruptible method breakdown ===\r\n");
2262 g_PiGcInfoSize.Log(LL_INFO10, "=== PartiallyInterruptible global breakdown ===\r\n");
2263 }
2264 LogSpew(LF_GCINFO, LL_INFO10, "Total SlimHeaders: %Iu\n", g_NumSlimHeaders);
2265 LogSpew(LF_GCINFO, LL_INFO10, "NumMethods: %Iu\n", g_NumFatHeaders);
2266#endif
2267}
2268
2269void GcInfoEncoder::SizeofSlotStateVarLengthVector(const BitArray &vector,
2270 UINT32 baseSkip,
2271 UINT32 baseRun,
2272 UINT32 *pSizeofSimple,
2273 UINT32 *pSizeofRLE,
2274 UINT32 *pSizeofRLENeg)
2275{
2276 // Try 3 different encodings
2277 UINT32 sizeofSimple = 1;
2278 UINT32 sizeofRLE;
2279 UINT32 sizeofRLENeg;
2280 for(UINT32 i = 0; i < m_NumSlots && !m_SlotTable[i].IsUntracked(); i++)
2281 {
2282 if(!m_SlotTable[i].IsDeleted())
2283 sizeofSimple++;
2284 }
2285
2286 if (sizeofSimple <= 2 + baseSkip + 1 + baseRun + 1)
2287 {
2288 // simple encoding is smaller than the smallest of the others
2289 // without even trying
2290 sizeofRLE = sizeofSimple + 1;
2291 sizeofRLENeg = sizeofSimple + 1;
2292 }
2293 else
2294 {
2295 sizeofRLE = 2; // For the header
2296 sizeofRLENeg = 2;
2297
2298 UINT32 rleStart = 0;
2299 bool fPrev = false;
2300 UINT32 i;
2301 for(i = 0; i < m_NumSlots && !m_SlotTable[i].IsUntracked(); i++)
2302 {
2303 if(!m_SlotTable[i].IsDeleted())
2304 {
2305 if (vector.ReadBit(i))
2306 {
2307 if (!fPrev)
2308 {
2309 // Skipping is done
2310 sizeofRLE += BitStreamWriter::SizeofVarLengthUnsigned(i - rleStart, baseSkip);
2311 sizeofRLENeg += BitStreamWriter::SizeofVarLengthUnsigned(i - rleStart, baseRun);
2312 rleStart = i + 1;
2313 fPrev = true;
2314 }
2315 }
2316 else
2317 {
2318 if (fPrev)
2319 {
2320 // Run is done
2321 sizeofRLE += BitStreamWriter::SizeofVarLengthUnsigned(i - rleStart, baseRun);
2322 sizeofRLENeg += BitStreamWriter::SizeofVarLengthUnsigned(i - rleStart, baseSkip);
2323 rleStart = i + 1;
2324 fPrev = false;
2325 }
2326 }
2327 }
2328 else
2329 {
2330 rleStart++;
2331 }
2332 }
2333
2334 _ASSERTE(i >= rleStart);
2335 sizeofRLE += BitStreamWriter::SizeofVarLengthUnsigned(i - rleStart, fPrev ? baseRun : baseSkip);
2336 sizeofRLENeg += BitStreamWriter::SizeofVarLengthUnsigned(i - rleStart, fPrev ? baseSkip : baseRun);
2337 }
2338
2339 *pSizeofSimple = sizeofSimple;
2340 *pSizeofRLE = sizeofRLE;
2341 *pSizeofRLENeg = sizeofRLENeg;
2342}
2343
2344UINT32 GcInfoEncoder::SizeofSlotStateVarLengthVector(const BitArray &vector,
2345 UINT32 baseSkip,
2346 UINT32 baseRun)
2347{
2348 // Try 3 different encodings
2349 UINT32 sizeofSimple;
2350 UINT32 sizeofRLE;
2351 UINT32 sizeofRLENeg;
2352 SizeofSlotStateVarLengthVector(vector, baseSkip, baseRun, &sizeofSimple, &sizeofRLE, &sizeofRLENeg);
2353
2354 if (sizeofSimple <= sizeofRLE && sizeofSimple <= sizeofRLENeg)
2355 return sizeofSimple;
2356 if (sizeofRLE <= sizeofRLENeg)
2357 return sizeofRLE;
2358 return sizeofRLENeg;
2359}
2360
2361UINT32 GcInfoEncoder::WriteSlotStateVarLengthVector(BitStreamWriter &writer,
2362 const BitArray &vector,
2363 UINT32 baseSkip,
2364 UINT32 baseRun)
2365{
2366 // Try 3 different encodings
2367 UINT32 sizeofSimple;
2368 UINT32 sizeofRLE;
2369 UINT32 sizeofRLENeg;
2370 SizeofSlotStateVarLengthVector(vector, baseSkip, baseRun, &sizeofSimple, &sizeofRLE, &sizeofRLENeg);
2371 UINT32 result;
2372
2373#ifdef _DEBUG
2374 size_t initial = writer.GetBitCount();
2375#endif // _DEBUG
2376
2377 if (sizeofSimple <= sizeofRLE && sizeofSimple <= sizeofRLENeg)
2378 {
2379 // Simple encoding is smallest
2380 writer.Write(0, 1);
2381 WriteSlotStateVector(writer, vector);
2382 result = sizeofSimple;
2383 }
2384 else
2385 {
2386 // One of the RLE encodings is the smallest
2387 writer.Write(1, 1);
2388
2389 if (sizeofRLENeg < sizeofRLE)
2390 {
2391 writer.Write(1, 1);
2392 UINT32 swap = baseSkip;
2393 baseSkip = baseRun;
2394 baseRun = swap;
2395 result = sizeofRLENeg;
2396 }
2397 else
2398 {
2399 writer.Write(0, 1);
2400 result = sizeofRLE;
2401 }
2402
2403
2404 UINT32 rleStart = 0;
2405 UINT32 i;
2406 bool fPrev = false;
2407 for(i = 0; i < m_NumSlots && !m_SlotTable[i].IsUntracked(); i++)
2408 {
2409 if(!m_SlotTable[i].IsDeleted())
2410 {
2411
2412 if (vector.ReadBit(i))
2413 {
2414 if (!fPrev)
2415 {
2416 // Skipping is done
2417 writer.EncodeVarLengthUnsigned(i - rleStart, baseSkip);
2418 rleStart = i + 1;
2419 fPrev = true;
2420 }
2421 }
2422 else
2423 {
2424 if (fPrev)
2425 {
2426 // Run is done
2427 writer.EncodeVarLengthUnsigned(i - rleStart, baseRun);
2428 rleStart = i + 1;
2429 fPrev = false;
2430 }
2431 }
2432 }
2433 else
2434 {
2435 rleStart++;
2436 }
2437 }
2438
2439 _ASSERTE(i >= rleStart);
2440 writer.EncodeVarLengthUnsigned(i - rleStart, fPrev ? baseRun : baseSkip);
2441 }
2442
2443#ifdef _DEBUG
2444 _ASSERTE(result + initial == writer.GetBitCount());
2445#endif // _DEBUG
2446
2447 return result;
2448}
2449
2450
2451void GcInfoEncoder::EliminateRedundantLiveDeadPairs(LifetimeTransition** ppTransitions,
2452 size_t* pNumTransitions,
2453 LifetimeTransition** ppEndTransitions)
2454{
2455 LifetimeTransition* pTransitions = *ppTransitions;
2456 LifetimeTransition* pEndTransitions = *ppEndTransitions;
2457
2458 LifetimeTransition* pNewTransitions = NULL;
2459 LifetimeTransition* pNewTransitionsCopyPtr = NULL;
2460 for (LifetimeTransition* pCurrent = pTransitions; pCurrent < pEndTransitions; pCurrent++)
2461 {
2462 // Is pCurrent the first of a dead/live pair?
2463 LifetimeTransition* pNext = pCurrent + 1;
2464 if (pNext < pEndTransitions &&
2465 pCurrent->CodeOffset == pNext->CodeOffset &&
2466 pCurrent->SlotId == pNext->SlotId &&
2467 pCurrent->IsDeleted == pNext->IsDeleted &&
2468 pCurrent->BecomesLive != pNext->BecomesLive)
2469 {
2470 // They are a pair we want to delete. If this is the first pair we've encountered, allocate
2471 // the new array:
2472 if (pNewTransitions == NULL)
2473 {
2474 pNewTransitions = (LifetimeTransition*)m_pAllocator->Alloc((*pNumTransitions) * sizeof(LifetimeTransition));
2475 pNewTransitionsCopyPtr = pNewTransitions;
2476 // Copy from the start up to (but not including) pCurrent...
2477 for (LifetimeTransition* pCopyPtr = pTransitions; pCopyPtr < pCurrent; pCopyPtr++, pNewTransitionsCopyPtr++)
2478 *pNewTransitionsCopyPtr = *pCopyPtr;
2479 }
2480 pCurrent++;
2481 }
2482 else
2483 {
2484 // pCurrent is not part of a pair. If we're copying, copy.
2485 if (pNewTransitionsCopyPtr != NULL)
2486 {
2487 *pNewTransitionsCopyPtr++ = *pCurrent;
2488 }
2489 }
2490 }
2491 // If we deleted any pairs, substitute the new array for the old.
2492 if (pNewTransitions != NULL)
2493 {
2494 m_pAllocator->Free(pTransitions);
2495 *ppTransitions = pNewTransitions;
2496 assert(pNewTransitionsCopyPtr != NULL);
2497 *ppEndTransitions = pNewTransitionsCopyPtr;
2498 *pNumTransitions = (*ppEndTransitions) - (*ppTransitions);
2499 }
2500}
2501
2502//
2503// Write encoded information to its final destination and frees temporary buffers.
2504// The encoder shouldn't be used anymore after calling this method.
2505//
2506BYTE* GcInfoEncoder::Emit()
2507{
2508 size_t cbGcInfoSize = m_Info1.GetByteCount() +
2509 m_Info2.GetByteCount();
2510
2511 LOG((LF_GCINFO, LL_INFO100, "GcInfoEncoder::Emit(): Size of GC info is %u bytes, code size %u bytes.\n", (unsigned)cbGcInfoSize, m_CodeLength ));
2512
2513 BYTE* destBuffer = (BYTE *)eeAllocGCInfo(cbGcInfoSize);
2514 // Allocator will throw an exception on failure.
2515 // NOTE: the returned pointer may not be aligned during ngen.
2516 _ASSERTE( destBuffer );
2517
2518 BYTE* ptr = destBuffer;
2519
2520 m_Info1.CopyTo( ptr );
2521 ptr += m_Info1.GetByteCount();
2522 m_Info1.Dispose();
2523
2524 m_Info2.CopyTo( ptr );
2525 ptr += m_Info2.GetByteCount();
2526 m_Info2.Dispose();
2527
2528#ifdef MUST_CALL_JITALLOCATOR_FREE
2529 m_pAllocator->Free( m_SlotTable );
2530#endif
2531
2532 return destBuffer;
2533}
2534
2535void * GcInfoEncoder::eeAllocGCInfo (size_t blockSize)
2536{
2537 return m_pCorJitInfo->allocGCInfo(blockSize);
2538}
2539
2540
2541BitStreamWriter::BitStreamWriter( IAllocator* pAllocator )
2542{
2543 m_pAllocator = pAllocator;
2544 m_BitCount = 0;
2545#ifdef _DEBUG
2546 m_MemoryBlocksCount = 0;
2547#endif
2548
2549 // Allocate memory blocks lazily
2550 m_OutOfBlockSlot = m_pCurrentSlot = (size_t*) NULL;
2551 m_FreeBitsInCurrentSlot = 0;
2552}
2553
2554//
2555// bit 0 is the least significative bit
2556// The stream encodes the first come bit in the least significant bit of each byte
2557//
2558void BitStreamWriter::Write( size_t data, UINT32 count )
2559{
2560 _ASSERT(count <= BITS_PER_SIZE_T);
2561
2562 if(count)
2563 {
2564 // Increment it now as we change count later on
2565 m_BitCount += count;
2566
2567 if( count > m_FreeBitsInCurrentSlot )
2568 {
2569 if( m_FreeBitsInCurrentSlot > 0 )
2570 {
2571 _ASSERTE(m_FreeBitsInCurrentSlot < BITS_PER_SIZE_T);
2572 WriteInCurrentSlot( data, m_FreeBitsInCurrentSlot );
2573 count -= m_FreeBitsInCurrentSlot;
2574 data >>= m_FreeBitsInCurrentSlot;
2575 }
2576
2577 _ASSERTE( count > 0 );
2578
2579 // Initialize the next slot
2580 if( ++m_pCurrentSlot >= m_OutOfBlockSlot )
2581 {
2582 // Get a new memory block
2583 AllocMemoryBlock();
2584 }
2585
2586 InitCurrentSlot();
2587
2588 // Write the remainder
2589 WriteInCurrentSlot( data, count );
2590 m_FreeBitsInCurrentSlot -= count;
2591 }
2592 else
2593 {
2594 WriteInCurrentSlot( data, count );
2595 m_FreeBitsInCurrentSlot -= count;
2596 // if m_FreeBitsInCurrentSlot becomes 0 a nwe slot will initialized on the next request
2597 }
2598 }
2599}
2600
2601
2602void BitStreamWriter::CopyTo( BYTE* buffer )
2603{
2604 int i,c;
2605 BYTE* source = NULL;
2606
2607 MemoryBlock* pMemBlock = m_MemoryBlocks.Head();
2608 if( pMemBlock == NULL )
2609 return;
2610
2611 while (pMemBlock->Next() != NULL)
2612 {
2613 source = (BYTE*) pMemBlock->Contents;
2614 // @TODO: use memcpy instead
2615 for( i = 0; i < m_MemoryBlockSize; i++ )
2616 {
2617 *( buffer++ ) = *( source++ );
2618 }
2619
2620 pMemBlock = pMemBlock->Next();
2621 }
2622
2623 source = (BYTE*) pMemBlock->Contents;
2624 // The number of bytes to copy in the last block
2625 c = (int) ((BYTE*) ( m_pCurrentSlot + 1 ) - source - m_FreeBitsInCurrentSlot/8);
2626 _ASSERTE( c >= 0 );
2627 // @TODO: use memcpy instead
2628 for( i = 0; i < c; i++ )
2629 {
2630 *( buffer++ ) = *( source++ );
2631 }
2632
2633}
2634
2635void BitStreamWriter::Dispose()
2636{
2637 m_MemoryBlocks.Dispose(m_pAllocator);
2638}
2639
2640int BitStreamWriter::SizeofVarLengthUnsigned( size_t n, UINT32 base)
2641{
2642 // If a value gets so big we are probably doing something wrong
2643 _ASSERTE(((INT32)(UINT32)n) >= 0);
2644 _ASSERTE((base > 0) && (base < BITS_PER_SIZE_T));
2645 size_t numEncodings = size_t{ 1 } << base;
2646 int bitsUsed;
2647 for(bitsUsed = base+1; ; bitsUsed += base+1)
2648 {
2649 if( n < numEncodings )
2650 {
2651 return bitsUsed;
2652 }
2653 else
2654 {
2655 n >>= base;
2656 }
2657 }
2658 return bitsUsed;
2659}
2660
2661int BitStreamWriter::EncodeVarLengthUnsigned( size_t n, UINT32 base)
2662{
2663 // If a value gets so big we are probably doing something wrong
2664 _ASSERTE(((INT32)(UINT32)n) >= 0);
2665 _ASSERTE((base > 0) && (base < BITS_PER_SIZE_T));
2666 size_t numEncodings = size_t{ 1 } << base;
2667 int bitsUsed;
2668 for(bitsUsed = base+1; ; bitsUsed += base+1)
2669 {
2670 if( n < numEncodings )
2671 {
2672 Write( n, base+1 ); // This sets the extension bit to zero
2673 return bitsUsed;
2674 }
2675 else
2676 {
2677 size_t currentChunk = n & (numEncodings-1);
2678 Write( currentChunk | numEncodings, base+1 );
2679 n >>= base;
2680 }
2681 }
2682 return bitsUsed;
2683}
2684
2685int BitStreamWriter::EncodeVarLengthSigned( SSIZE_T n, UINT32 base )
2686{
2687 _ASSERTE((base > 0) && (base < BITS_PER_SIZE_T));
2688 size_t numEncodings = size_t{ 1 } << base;
2689 for(int bitsUsed = base+1; ; bitsUsed += base+1)
2690 {
2691 size_t currentChunk = ((size_t) n) & (numEncodings-1);
2692 size_t topmostBit = currentChunk & (numEncodings >> 1);
2693 n >>= base; // signed arithmetic shift
2694 if((topmostBit && (n == (SSIZE_T)-1)) || (!topmostBit && (n == 0)))
2695 {
2696 // The topmost bit correctly represents the sign
2697 Write( currentChunk, base+1 ); // This sets the extension bit to zero
2698 return bitsUsed;
2699 }
2700 else
2701 {
2702 Write( currentChunk | numEncodings, base+1 );
2703 }
2704 }
2705}
2706
2707