| 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 | * GC Information Encoding API |
| 7 | * |
| 8 | *****************************************************************/ |
| 9 | |
| 10 | /***************************************************************** |
| 11 | |
| 12 | ENCODING LAYOUT |
| 13 | |
| 14 | 1. Header |
| 15 | |
| 16 | Slim Header for simple and common cases: |
| 17 | - EncodingType[Slim] |
| 18 | - ReturnKind (Fat: 2 bits) |
| 19 | - CodeLength |
| 20 | - NumCallSites (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED) |
| 21 | |
| 22 | Fat Header for other cases: |
| 23 | - EncodingType[Fat] |
| 24 | - Flag: isVarArg, |
| 25 | hasSecurityObject, |
| 26 | hasGSCookie, |
| 27 | hasPSPSymStackSlot, |
| 28 | hasGenericsInstContextStackSlot, |
| 29 | hasStackBaseregister, |
| 30 | wantsReportOnlyLeaf (AMD64 use only), |
| 31 | hasTailCalls (ARM/ARM64 only) |
| 32 | hasSizeOfEditAndContinuePreservedArea |
| 33 | hasReversePInvokeFrame, |
| 34 | - ReturnKind (Fat: 4 bits) |
| 35 | - CodeLength |
| 36 | - Prolog (if hasSecurityObject || hasGenericsInstContextStackSlot || hasGSCookie) |
| 37 | - Epilog (if hasGSCookie) |
| 38 | - SecurityObjectStackSlot (if any) |
| 39 | - GSCookieStackSlot (if any) |
| 40 | - PSPSymStackSlot (if any) |
| 41 | - GenericsInstContextStackSlot (if any) |
| 42 | - StackBaseRegister (if any) |
| 43 | - SizeOfEditAndContinuePreservedArea (if any) |
| 44 | - ReversePInvokeFrameSlot (if any) |
| 45 | - SizeOfStackOutgoingAndScratchArea (#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA) |
| 46 | - NumCallSites (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED) |
| 47 | - NumInterruptibleRanges |
| 48 | |
| 49 | 2. Call sites offsets (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED) |
| 50 | 3. Fully-interruptible ranges |
| 51 | 4. Slot table |
| 52 | 5. GC state at call sites (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED) |
| 53 | 6. GC state at try clauses (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED) |
| 54 | 7. Chunk pointers |
| 55 | 8. Chunk encodings |
| 56 | |
| 57 | |
| 58 | STANDALONE_BUILD |
| 59 | |
| 60 | The STANDALONE_BUILD switch can be used to build the GcInfoEncoder library |
| 61 | independently by clients outside the CoreClr tree. |
| 62 | |
| 63 | The GcInfo library uses some custom data-structures (ex: ArrayList, SimplerHashTable) |
| 64 | and includes some utility libraries (ex: UtilCode) which pull in several other |
| 65 | headers with considerable unrelated content. Rather than porting all the |
| 66 | utility code to suite other clients, the STANDALONE_BUILD switch can be used |
| 67 | to include only the minimal set of headers specific to GcInfo encodings. |
| 68 | |
| 69 | Clients of STANDALONE_BUILD will likely use standard library |
| 70 | implementations of data-structures like ArrayList, HashMap etc., in place |
| 71 | of the custom implementation currently used by GcInfoEncoder. |
| 72 | |
| 73 | Rather than spew the GcInfoEnoder code with |
| 74 | #ifdef STANDALONE_BUILD ... #else .. #endif blocks, we include a special |
| 75 | header GcInfoUtil.h in STANDALONE_BUILD mode. GcInfoUtil.h is expected to |
| 76 | supply the interface/implementation for the data-structures and utilities |
| 77 | used by GcInfoEncoder. This header should be provided by the clients doing |
| 78 | the standalone build in their source tree. |
| 79 | |
| 80 | *****************************************************************/ |
| 81 | |
| 82 | |
| 83 | #ifndef __GCINFOENCODER_H__ |
| 84 | #define __GCINFOENCODER_H__ |
| 85 | |
| 86 | #ifdef STANDALONE_BUILD |
| 87 | #include <wchar.h> |
| 88 | #include <stdio.h> |
| 89 | #include "GcInfoUtil.h" |
| 90 | #include "corjit.h" |
| 91 | #else |
| 92 | #include <windows.h> |
| 93 | #include <wchar.h> |
| 94 | #include <stdio.h> |
| 95 | #include "corjit.h" |
| 96 | #include "iallocator.h" |
| 97 | #include "gcinfoarraylist.h" |
| 98 | #include "stdmacros.h" |
| 99 | #include "eexcp.h" |
| 100 | #endif |
| 101 | |
| 102 | #include "gcinfotypes.h" |
| 103 | |
| 104 | // As stated in issue #6008, GcInfoSize should be incorporated into debug builds. |
| 105 | #ifdef _DEBUG |
| 106 | #define MEASURE_GCINFO |
| 107 | #endif |
| 108 | |
| 109 | #ifdef MEASURE_GCINFO |
| 110 | struct GcInfoSize |
| 111 | { |
| 112 | size_t TotalSize; |
| 113 | |
| 114 | size_t NumMethods; |
| 115 | size_t NumCallSites; |
| 116 | size_t NumRanges; |
| 117 | size_t NumRegs; |
| 118 | size_t NumStack; |
| 119 | size_t NumUntracked; |
| 120 | size_t NumTransitions; |
| 121 | size_t SizeOfCode; |
| 122 | size_t EncPreservedSlots; |
| 123 | |
| 124 | size_t UntrackedSlotSize; |
| 125 | size_t NumUntrackedSize; |
| 126 | size_t FlagsSize; |
| 127 | size_t RetKindSize; |
| 128 | size_t CodeLengthSize; |
| 129 | size_t ProEpilogSize; |
| 130 | size_t SecObjSize; |
| 131 | size_t GsCookieSize; |
| 132 | size_t PspSymSize; |
| 133 | size_t GenericsCtxSize; |
| 134 | size_t StackBaseSize; |
| 135 | size_t ReversePInvokeFrameSize; |
| 136 | size_t FixedAreaSize; |
| 137 | size_t NumCallSitesSize; |
| 138 | size_t NumRangesSize; |
| 139 | size_t CallSitePosSize; |
| 140 | size_t RangeSize; |
| 141 | size_t NumRegsSize; |
| 142 | size_t NumStackSize; |
| 143 | size_t RegSlotSize; |
| 144 | size_t StackSlotSize; |
| 145 | size_t CallSiteStateSize; |
| 146 | size_t EhPosSize; |
| 147 | size_t EhStateSize; |
| 148 | size_t ChunkPtrSize; |
| 149 | size_t ChunkMaskSize; |
| 150 | size_t ChunkFinalStateSize; |
| 151 | size_t ChunkTransitionSize; |
| 152 | |
| 153 | GcInfoSize(); |
| 154 | GcInfoSize& operator+=(const GcInfoSize& other); |
| 155 | void Log(DWORD level, const char * ); |
| 156 | }; |
| 157 | #endif |
| 158 | |
| 159 | struct GcSlotDesc |
| 160 | { |
| 161 | union |
| 162 | { |
| 163 | UINT32 RegisterNumber; |
| 164 | GcStackSlot Stack; |
| 165 | } Slot; |
| 166 | GcSlotFlags Flags; |
| 167 | |
| 168 | BOOL IsRegister() const |
| 169 | { |
| 170 | return (Flags & GC_SLOT_IS_REGISTER); |
| 171 | } |
| 172 | BOOL IsInterior() const |
| 173 | { |
| 174 | return (Flags & GC_SLOT_INTERIOR); |
| 175 | } |
| 176 | BOOL IsPinned() const |
| 177 | { |
| 178 | return (Flags & GC_SLOT_PINNED); |
| 179 | } |
| 180 | BOOL IsUntracked() const |
| 181 | { |
| 182 | return (Flags & GC_SLOT_UNTRACKED); |
| 183 | } |
| 184 | BOOL IsDeleted() const |
| 185 | { |
| 186 | return (Flags & GC_SLOT_IS_DELETED); |
| 187 | } |
| 188 | void MarkDeleted() |
| 189 | { |
| 190 | Flags = (GcSlotFlags) (Flags | GC_SLOT_IS_DELETED); |
| 191 | } |
| 192 | }; |
| 193 | |
| 194 | class BitArray; |
| 195 | class BitStreamWriter |
| 196 | { |
| 197 | public: |
| 198 | BitStreamWriter( IAllocator* pAllocator ); |
| 199 | |
| 200 | // bit 0 is the least significative bit |
| 201 | void Write( size_t data, UINT32 count ); |
| 202 | |
| 203 | inline size_t GetBitCount() |
| 204 | { |
| 205 | return m_BitCount; |
| 206 | } |
| 207 | |
| 208 | inline size_t GetByteCount() |
| 209 | { |
| 210 | return ( m_BitCount + 7 ) / 8; |
| 211 | } |
| 212 | |
| 213 | |
| 214 | void CopyTo( BYTE* buffer ); |
| 215 | void Dispose(); |
| 216 | |
| 217 | //-------------------------------------------------------- |
| 218 | // Compute the number of bits used to encode variable length numbers |
| 219 | // Uses base+1 bits at minimum |
| 220 | // Bits 0..(base-1) represent the encoded quantity |
| 221 | // If it doesn't fit, set bit #base to 1 and use base+1 more bits |
| 222 | //-------------------------------------------------------- |
| 223 | static int SizeofVarLengthUnsigned( size_t n, UINT32 base ); |
| 224 | |
| 225 | //-------------------------------------------------------- |
| 226 | // Encode variable length numbers |
| 227 | // Uses base+1 bits at minimum |
| 228 | // Bits 0..(base-1) represent the encoded quantity |
| 229 | // If it doesn't fit, set bit #base to 1 and use base+1 more bits |
| 230 | //-------------------------------------------------------- |
| 231 | int EncodeVarLengthUnsigned( size_t n, UINT32 base ); |
| 232 | |
| 233 | //-------------------------------------------------------- |
| 234 | // Signed quantities are encoded the same as unsigned |
| 235 | // The most relevant difference is that a number is considered |
| 236 | // to fit in base bits if the topmost bit of a base-long chunk |
| 237 | // matches the sign of the whole number |
| 238 | //-------------------------------------------------------- |
| 239 | int EncodeVarLengthSigned( SSIZE_T n, UINT32 base ); |
| 240 | |
| 241 | private: |
| 242 | class MemoryBlockList; |
| 243 | class MemoryBlock |
| 244 | { |
| 245 | friend class MemoryBlockList; |
| 246 | MemoryBlock* m_next; |
| 247 | |
| 248 | public: |
| 249 | size_t Contents[]; |
| 250 | |
| 251 | inline MemoryBlock* Next() |
| 252 | { |
| 253 | return m_next; |
| 254 | } |
| 255 | }; |
| 256 | |
| 257 | class MemoryBlockList |
| 258 | { |
| 259 | MemoryBlock* m_head; |
| 260 | MemoryBlock* m_tail; |
| 261 | |
| 262 | public: |
| 263 | MemoryBlockList(); |
| 264 | |
| 265 | inline MemoryBlock* Head() |
| 266 | { |
| 267 | return m_head; |
| 268 | } |
| 269 | |
| 270 | MemoryBlock* AppendNew(IAllocator* allocator, size_t bytes); |
| 271 | void Dispose(IAllocator* allocator); |
| 272 | }; |
| 273 | |
| 274 | IAllocator* m_pAllocator; |
| 275 | size_t m_BitCount; |
| 276 | UINT32 m_FreeBitsInCurrentSlot; |
| 277 | MemoryBlockList m_MemoryBlocks; |
| 278 | const static int m_MemoryBlockSize = 128; // must be a multiple of the pointer size |
| 279 | size_t* m_pCurrentSlot; // bits are written through this pointer |
| 280 | size_t* m_OutOfBlockSlot; // sentinel value to determine when the block is full |
| 281 | #ifdef _DEBUG |
| 282 | int m_MemoryBlocksCount; |
| 283 | #endif |
| 284 | |
| 285 | private: |
| 286 | // Writes bits knowing that they will all fit in the current memory slot |
| 287 | inline void WriteInCurrentSlot( size_t data, UINT32 count ) |
| 288 | { |
| 289 | data &= SAFE_SHIFT_LEFT(1, count) - 1; |
| 290 | data <<= (BITS_PER_SIZE_T - m_FreeBitsInCurrentSlot); |
| 291 | *m_pCurrentSlot |= data; |
| 292 | } |
| 293 | |
| 294 | inline void AllocMemoryBlock() |
| 295 | { |
| 296 | _ASSERTE( IS_ALIGNED( m_MemoryBlockSize, sizeof( size_t ) ) ); |
| 297 | MemoryBlock* pMemBlock = m_MemoryBlocks.AppendNew(m_pAllocator, m_MemoryBlockSize); |
| 298 | |
| 299 | m_pCurrentSlot = pMemBlock->Contents; |
| 300 | m_OutOfBlockSlot = m_pCurrentSlot + m_MemoryBlockSize / sizeof( size_t ); |
| 301 | |
| 302 | #ifdef _DEBUG |
| 303 | m_MemoryBlocksCount++; |
| 304 | #endif |
| 305 | |
| 306 | } |
| 307 | |
| 308 | inline void InitCurrentSlot() |
| 309 | { |
| 310 | m_FreeBitsInCurrentSlot = BITS_PER_SIZE_T; |
| 311 | *m_pCurrentSlot = 0; |
| 312 | } |
| 313 | }; |
| 314 | |
| 315 | |
| 316 | typedef UINT32 GcSlotId; |
| 317 | |
| 318 | |
| 319 | inline UINT32 GetNormCodeOffsetChunk(UINT32 normCodeOffset) |
| 320 | { |
| 321 | return normCodeOffset / NUM_NORM_CODE_OFFSETS_PER_CHUNK; |
| 322 | } |
| 323 | |
| 324 | inline UINT32 GetCodeOffsetChunk(UINT32 codeOffset) |
| 325 | { |
| 326 | return (NORMALIZE_CODE_OFFSET(codeOffset)) / NUM_NORM_CODE_OFFSETS_PER_CHUNK; |
| 327 | } |
| 328 | |
| 329 | enum GENERIC_CONTEXTPARAM_TYPE |
| 330 | { |
| 331 | GENERIC_CONTEXTPARAM_NONE = 0, |
| 332 | GENERIC_CONTEXTPARAM_MT = 1, |
| 333 | GENERIC_CONTEXTPARAM_MD = 2, |
| 334 | GENERIC_CONTEXTPARAM_THIS = 3, |
| 335 | }; |
| 336 | |
| 337 | extern void DECLSPEC_NORETURN ThrowOutOfMemory(); |
| 338 | |
| 339 | class GcInfoEncoder |
| 340 | { |
| 341 | public: |
| 342 | typedef void (*NoMemoryFunction)(void); |
| 343 | |
| 344 | GcInfoEncoder( |
| 345 | ICorJitInfo* pCorJitInfo, |
| 346 | CORINFO_METHOD_INFO* pMethodInfo, |
| 347 | IAllocator* pJitAllocator, |
| 348 | NoMemoryFunction pNoMem = ::ThrowOutOfMemory |
| 349 | ); |
| 350 | |
| 351 | struct LifetimeTransition |
| 352 | { |
| 353 | UINT32 CodeOffset; |
| 354 | GcSlotId SlotId; |
| 355 | BYTE BecomesLive; |
| 356 | BYTE IsDeleted; |
| 357 | }; |
| 358 | |
| 359 | |
| 360 | #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED |
| 361 | void DefineCallSites(UINT32* pCallSites, BYTE* pCallSiteSizes, UINT32 numCallSites); |
| 362 | #endif |
| 363 | |
| 364 | //------------------------------------------------------------------------ |
| 365 | // Interruptibility |
| 366 | //------------------------------------------------------------------------ |
| 367 | |
| 368 | // An instruction at offset x will be interruptible |
| 369 | // if-and-only-if startInstructionOffset <= x < startInstructionOffset+length |
| 370 | void DefineInterruptibleRange( UINT32 startInstructionOffset, UINT32 length ); |
| 371 | |
| 372 | |
| 373 | //------------------------------------------------------------------------ |
| 374 | // Slot information |
| 375 | //------------------------------------------------------------------------ |
| 376 | |
| 377 | // |
| 378 | // If spOffset is relative to the current SP, spOffset must be non-negative. |
| 379 | // If spOffset is relative to the SP of the caller (same as SP at the method entry and exit) |
| 380 | // Negative offsets describe GC refs in the local and outgoing areas. |
| 381 | // Positive offsets describe GC refs in the scratch area |
| 382 | // Note that if the dynamic allocation area is resized, the outgoing area will not be valid anymore |
| 383 | // Old slots must be declared dead and new ones can be defined. |
| 384 | // It's up to the JIT to do the right thing. We don't enforce this. |
| 385 | |
| 386 | GcSlotId GetRegisterSlotId( UINT32 regNum, GcSlotFlags flags ); |
| 387 | GcSlotId GetStackSlotId( INT32 spOffset, GcSlotFlags flags, GcStackSlotBase spBase = GC_CALLER_SP_REL ); |
| 388 | |
| 389 | // |
| 390 | // After a FinalizeSlotIds is called, no more slot definitions can be made. |
| 391 | // FinalizeSlotIds must be called once and only once before calling Build() |
| 392 | // |
| 393 | void FinalizeSlotIds(); |
| 394 | |
| 395 | |
| 396 | //------------------------------------------------------------------------ |
| 397 | // Fully-interruptible information |
| 398 | //------------------------------------------------------------------------ |
| 399 | |
| 400 | // |
| 401 | // For inputs, pass zero as offset |
| 402 | // |
| 403 | |
| 404 | // Indicates that the GC state of slot "slotId" becomes (and remains, until another transition) |
| 405 | // "slotState" after the instruction preceding "instructionOffset" (so it is first in this state when |
| 406 | // the IP of a suspended thread is at this instruction offset). |
| 407 | |
| 408 | void SetSlotState( UINT32 instructionOffset, |
| 409 | GcSlotId slotId, |
| 410 | GcSlotState slotState |
| 411 | ); |
| 412 | |
| 413 | |
| 414 | //------------------------------------------------------------------------ |
| 415 | // ReturnKind |
| 416 | //------------------------------------------------------------------------ |
| 417 | |
| 418 | void SetReturnKind(ReturnKind returnKind); |
| 419 | |
| 420 | //------------------------------------------------------------------------ |
| 421 | // Miscellaneous method information |
| 422 | //------------------------------------------------------------------------ |
| 423 | |
| 424 | void SetSecurityObjectStackSlot( INT32 spOffset ); |
| 425 | void SetPrologSize( UINT32 prologSize ); |
| 426 | void SetGSCookieStackSlot( INT32 spOffsetGSCookie, UINT32 validRangeStart, UINT32 validRangeEnd ); |
| 427 | void SetPSPSymStackSlot( INT32 spOffsetPSPSym ); |
| 428 | void SetGenericsInstContextStackSlot( INT32 spOffsetGenericsContext, GENERIC_CONTEXTPARAM_TYPE type); |
| 429 | void SetReversePInvokeFrameSlot(INT32 spOffset); |
| 430 | void SetIsVarArg(); |
| 431 | void SetCodeLength( UINT32 length ); |
| 432 | |
| 433 | // Optional in the general case. Required if the method uses GC_FRAMEREG_REL stack slots |
| 434 | void SetStackBaseRegister( UINT32 registerNumber ); |
| 435 | |
| 436 | // Number of slots preserved during EnC remap |
| 437 | void SetSizeOfEditAndContinuePreservedArea( UINT32 size ); |
| 438 | |
| 439 | #ifdef _TARGET_AMD64_ |
| 440 | // Used to only report a frame once for the leaf function/funclet |
| 441 | // instead of once for each live function/funclet on the stack. |
| 442 | // Called only by RyuJIT (not JIT64) |
| 443 | void SetWantsReportOnlyLeaf(); |
| 444 | #elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) |
| 445 | void SetHasTailCalls(); |
| 446 | #endif // _TARGET_AMD64_ |
| 447 | |
| 448 | #ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA |
| 449 | void SetSizeOfStackOutgoingAndScratchArea( UINT32 size ); |
| 450 | #endif // FIXED_STACK_PARAMETER_SCRATCH_AREA |
| 451 | |
| 452 | |
| 453 | //------------------------------------------------------------------------ |
| 454 | // Encoding |
| 455 | //------------------------------------------------------------------------ |
| 456 | |
| 457 | // |
| 458 | // Build() encodes GC information into temporary buffers. |
| 459 | // The method description cannot change after Build is called |
| 460 | // |
| 461 | void Build(); |
| 462 | |
| 463 | // |
| 464 | // Write encoded information to its final destination and frees temporary buffers. |
| 465 | // The encoder shouldn't be used anymore after calling this method. |
| 466 | // It returns a pointer to the destination buffer, which address is byte-aligned |
| 467 | // |
| 468 | BYTE* Emit(); |
| 469 | |
| 470 | private: |
| 471 | |
| 472 | friend int __cdecl CompareLifetimeTransitionsByOffsetThenSlot(const void*, const void*); |
| 473 | friend int CompareLifetimeTransitionsByChunk(const void*, const void*); |
| 474 | |
| 475 | |
| 476 | struct InterruptibleRange |
| 477 | { |
| 478 | UINT32 NormStartOffset; |
| 479 | UINT32 NormStopOffset; |
| 480 | }; |
| 481 | |
| 482 | ICorJitInfo* m_pCorJitInfo; |
| 483 | CORINFO_METHOD_INFO* m_pMethodInfo; |
| 484 | IAllocator* m_pAllocator; |
| 485 | NoMemoryFunction m_pNoMem; |
| 486 | |
| 487 | #ifdef _DEBUG |
| 488 | const char *m_MethodName, *m_ModuleName; |
| 489 | #endif |
| 490 | |
| 491 | BitStreamWriter m_Info1; // Used for everything except for chunk encodings |
| 492 | BitStreamWriter m_Info2; // Used for chunk encodings |
| 493 | |
| 494 | GcInfoArrayList<InterruptibleRange, 8> m_InterruptibleRanges; |
| 495 | GcInfoArrayList<LifetimeTransition, 64> m_LifetimeTransitions; |
| 496 | |
| 497 | bool m_IsVarArg; |
| 498 | #if defined(_TARGET_AMD64_) |
| 499 | bool m_WantsReportOnlyLeaf; |
| 500 | #elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) |
| 501 | bool m_HasTailCalls; |
| 502 | #endif // _TARGET_AMD64_ |
| 503 | INT32 m_SecurityObjectStackSlot; |
| 504 | INT32 m_GSCookieStackSlot; |
| 505 | UINT32 m_GSCookieValidRangeStart; |
| 506 | UINT32 m_GSCookieValidRangeEnd; |
| 507 | INT32 m_PSPSymStackSlot; |
| 508 | INT32 m_GenericsInstContextStackSlot; |
| 509 | GENERIC_CONTEXTPARAM_TYPE m_contextParamType; |
| 510 | ReturnKind m_ReturnKind; |
| 511 | UINT32 m_CodeLength; |
| 512 | UINT32 m_StackBaseRegister; |
| 513 | UINT32 m_SizeOfEditAndContinuePreservedArea; |
| 514 | INT32 m_ReversePInvokeFrameSlot; |
| 515 | InterruptibleRange* m_pLastInterruptibleRange; |
| 516 | |
| 517 | #ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA |
| 518 | UINT32 m_SizeOfStackOutgoingAndScratchArea; |
| 519 | #endif // FIXED_STACK_PARAMETER_SCRATCH_AREA |
| 520 | |
| 521 | void * eeAllocGCInfo (size_t blockSize); |
| 522 | |
| 523 | private: |
| 524 | |
| 525 | friend class EncoderCheckState; |
| 526 | |
| 527 | static const UINT32 m_SlotTableInitialSize = 32; |
| 528 | UINT32 m_SlotTableSize; |
| 529 | UINT32 m_NumSlots; |
| 530 | GcSlotDesc *m_SlotTable; |
| 531 | |
| 532 | #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED |
| 533 | UINT32* m_pCallSites; |
| 534 | BYTE* m_pCallSiteSizes; |
| 535 | UINT32 m_NumCallSites; |
| 536 | #endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED |
| 537 | |
| 538 | void GrowSlotTable(); |
| 539 | |
| 540 | void WriteSlotStateVector(BitStreamWriter &writer, const BitArray& vector); |
| 541 | |
| 542 | UINT32 SizeofSlotStateVarLengthVector(const BitArray& vector, UINT32 baseSkip, UINT32 baseRun); |
| 543 | void SizeofSlotStateVarLengthVector(const BitArray& vector, UINT32 baseSkip, UINT32 baseRun, UINT32 * pSizeofSimple, UINT32 * pSizeofRLE, UINT32 * pSizeofRLENeg); |
| 544 | UINT32 WriteSlotStateVarLengthVector(BitStreamWriter &writer, const BitArray& vector, UINT32 baseSkip, UINT32 baseRun); |
| 545 | |
| 546 | bool IsAlwaysScratch(GcSlotDesc &slot); |
| 547 | |
| 548 | // Assumes that "*ppTransitions" is has size "numTransitions", is sorted by CodeOffset then by SlotId, |
| 549 | // and that "*ppEndTransitions" points one beyond the end of the array. If "*ppTransitions" contains |
| 550 | // any dead/live transitions pairs for the same CodeOffset and SlotID, removes those, by allocating a |
| 551 | // new array, and copying the non-removed elements into it. If it does this, sets "*ppTransitions" to |
| 552 | // point to the new array, "*pNumTransitions" to its shorted length, and "*ppEndTransitions" to |
| 553 | // point one beyond the used portion of this array. |
| 554 | void EliminateRedundantLiveDeadPairs(LifetimeTransition** ppTransitions, |
| 555 | size_t* pNumTransitions, |
| 556 | LifetimeTransition** ppEndTransitions); |
| 557 | |
| 558 | #ifdef _DEBUG |
| 559 | bool m_IsSlotTableFrozen; |
| 560 | #endif |
| 561 | |
| 562 | #ifdef MEASURE_GCINFO |
| 563 | GcInfoSize m_CurrentMethodSize; |
| 564 | #endif |
| 565 | }; |
| 566 | |
| 567 | #endif // !__GCINFOENCODER_H__ |
| 568 | |