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#include "common.h"
7
8#include "gcinfodecoder.h"
9
10#ifdef USE_GC_INFO_DECODER
11
12#ifndef CHECK_APP_DOMAIN
13#define CHECK_APP_DOMAIN 0
14#endif
15
16#ifndef GCINFODECODER_CONTRACT
17#define GCINFODECODER_CONTRACT LIMITED_METHOD_CONTRACT
18#endif // !GCINFODECODER_CONTRACT
19
20
21#ifndef GET_CALLER_SP
22#define GET_CALLER_SP(pREGDISPLAY) EECodeManager::GetCallerSp(pREGDISPLAY)
23#endif // !GET_CALLER_SP
24
25#ifndef VALIDATE_OBJECTREF
26#if defined(DACCESS_COMPILE) || defined(CROSSGEN_COMPILE)
27#define VALIDATE_OBJECTREF(objref, fDeep)
28#else // DACCESS_COMPILE || CROSSGEN_COMPILE
29#define VALIDATE_OBJECTREF(objref, fDeep) OBJECTREF_TO_UNCHECKED_OBJECTREF(objref)->Validate(fDeep)
30#endif // DACCESS_COMPILE || CROSSGEN_COMPILE
31#endif // !VALIDATE_OBJECTREF
32
33#ifndef VALIDATE_ROOT
34#include "gcenv.h"
35#define VALIDATE_ROOT(isInterior, hCallBack, pObjRef) \
36 do { \
37 /* Only call Object::Validate() with bDeep == TRUE if we are in the promote phase. */ \
38 /* We should call Validate() with bDeep == FALSE if we are in the relocation phase. */ \
39 /* Actually with the introduction of the POPO feature, we cannot validate during */ \
40 /* relocate because POPO might have written over the object. It will require non */ \
41 /* trivial amount of work to make this work.*/ \
42 \
43 GCCONTEXT* pGCCtx = (GCCONTEXT*)(hCallBack); \
44 \
45 if (!(isInterior) && !(m_Flags & DECODE_NO_VALIDATION) && (pGCCtx->sc->promotion)) { \
46 VALIDATE_OBJECTREF(*(pObjRef), pGCCtx->sc->promotion == TRUE); \
47 } \
48 } while (0)
49#endif // !VALIDATE_ROOT
50
51#ifndef LOG_PIPTR
52#define LOG_PIPTR(pObjRef, gcFlags, hCallBack) \
53 { \
54 GCCONTEXT* pGCCtx = (GCCONTEXT*)(hCallBack); \
55 if (pGCCtx->sc->promotion) \
56 { \
57 LOG((LF_GCROOTS, LL_INFO1000, /* Part Three */ \
58 LOG_PIPTR_OBJECT_CLASS(OBJECTREF_TO_UNCHECKED_OBJECTREF(*pObjRef), (gcFlags & GC_CALL_PINNED), (gcFlags & GC_CALL_INTERIOR)))); \
59 } \
60 else \
61 { \
62 LOG((LF_GCROOTS, LL_INFO1000, /* Part Three */ \
63 LOG_PIPTR_OBJECT(OBJECTREF_TO_UNCHECKED_OBJECTREF(*pObjRef), (gcFlags & GC_CALL_PINNED), (gcFlags & GC_CALL_INTERIOR)))); \
64 } \
65 }
66#endif // !LOG_PIPTR
67
68bool GcInfoDecoder::SetIsInterruptibleCB (UINT32 startOffset, UINT32 stopOffset, void * hCallback)
69{
70 GcInfoDecoder *pThis = (GcInfoDecoder*)hCallback;
71
72
73 bool fStop = pThis->m_InstructionOffset >= startOffset && pThis->m_InstructionOffset < stopOffset;
74
75 if (fStop)
76 pThis->m_IsInterruptible = true;
77
78 return fStop;
79}
80
81GcInfoDecoder::GcInfoDecoder(
82 GCInfoToken gcInfoToken,
83 GcInfoDecoderFlags flags,
84 UINT32 breakOffset
85 )
86 : m_Reader(dac_cast<PTR_CBYTE>(gcInfoToken.Info))
87 , m_InstructionOffset(breakOffset)
88 , m_IsInterruptible(false)
89 , m_ReturnKind(RT_Illegal)
90#ifdef _DEBUG
91 , m_Flags( flags )
92 , m_GcInfoAddress(dac_cast<PTR_CBYTE>(gcInfoToken.Info))
93#endif
94 , m_Version(gcInfoToken.Version)
95{
96 _ASSERTE( (flags & (DECODE_INTERRUPTIBILITY | DECODE_GC_LIFETIMES)) || (0 == breakOffset) );
97
98 // The current implementation doesn't support the two flags together
99 _ASSERTE(
100 ((flags & (DECODE_INTERRUPTIBILITY | DECODE_GC_LIFETIMES)) != (DECODE_INTERRUPTIBILITY | DECODE_GC_LIFETIMES))
101 );
102
103 //--------------------------------------------
104 // Pre-decode information
105 //--------------------------------------------
106
107 GcInfoHeaderFlags headerFlags;
108 bool slimHeader = (m_Reader.ReadOneFast() == 0);
109
110 if (slimHeader)
111 {
112 headerFlags = (GcInfoHeaderFlags)(m_Reader.ReadOneFast() ? GC_INFO_HAS_STACK_BASE_REGISTER : 0);
113 }
114 else
115 {
116 int numFlagBits = (m_Version == 1) ? GC_INFO_FLAGS_BIT_SIZE_VERSION_1 : GC_INFO_FLAGS_BIT_SIZE;
117 headerFlags = (GcInfoHeaderFlags) m_Reader.Read(numFlagBits);
118 }
119
120 m_IsVarArg = headerFlags & GC_INFO_IS_VARARG;
121 int hasSecurityObject = headerFlags & GC_INFO_HAS_SECURITY_OBJECT;
122 int hasGSCookie = headerFlags & GC_INFO_HAS_GS_COOKIE;
123 int hasPSPSym = headerFlags & GC_INFO_HAS_PSP_SYM;
124 int hasGenericsInstContext = (headerFlags & GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK) != GC_INFO_HAS_GENERICS_INST_CONTEXT_NONE;
125 m_GenericSecretParamIsMD = (headerFlags & GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK) == GC_INFO_HAS_GENERICS_INST_CONTEXT_MD;
126 m_GenericSecretParamIsMT = (headerFlags & GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK) == GC_INFO_HAS_GENERICS_INST_CONTEXT_MT;
127 int hasStackBaseRegister = headerFlags & GC_INFO_HAS_STACK_BASE_REGISTER;
128#ifdef _TARGET_AMD64_
129 m_WantsReportOnlyLeaf = ((headerFlags & GC_INFO_WANTS_REPORT_ONLY_LEAF) != 0);
130#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
131 m_HasTailCalls = ((headerFlags & GC_INFO_HAS_TAILCALLS) != 0);
132#endif // _TARGET_AMD64_
133 int hasSizeOfEditAndContinuePreservedArea = headerFlags & GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS;
134
135 int hasReversePInvokeFrame = false;
136 if (gcInfoToken.IsReversePInvokeFrameAvailable())
137 {
138 hasReversePInvokeFrame = headerFlags & GC_INFO_REVERSE_PINVOKE_FRAME;
139 }
140
141 if (gcInfoToken.IsReturnKindAvailable())
142 {
143 int returnKindBits = (slimHeader) ? SIZE_OF_RETURN_KIND_IN_SLIM_HEADER : SIZE_OF_RETURN_KIND_IN_FAT_HEADER;
144 m_ReturnKind =
145 (ReturnKind)((UINT32)m_Reader.Read(returnKindBits));
146 }
147 else
148 {
149#ifndef _TARGET_X86_
150 m_ReturnKind = RT_Unset;
151#endif // ! _TARGET_X86_
152 }
153
154 if (flags == DECODE_RETURN_KIND) {
155 // Bail, if we've decoded enough,
156 return;
157 }
158
159 m_CodeLength = (UINT32) DENORMALIZE_CODE_LENGTH((UINT32) m_Reader.DecodeVarLengthUnsigned(CODE_LENGTH_ENCBASE));
160
161 if (flags == DECODE_CODE_LENGTH) {
162 // Bail, if we've decoded enough,
163 return;
164 }
165
166 if (hasGSCookie)
167 {
168 // Note that normalization as a code offset can be different than
169 // normalization as code legnth
170 UINT32 normCodeLength = NORMALIZE_CODE_OFFSET(m_CodeLength);
171
172 // Decode prolog/epilog information
173 UINT32 normPrologSize = (UINT32) m_Reader.DecodeVarLengthUnsigned(NORM_PROLOG_SIZE_ENCBASE) + 1;
174 UINT32 normEpilogSize = (UINT32) m_Reader.DecodeVarLengthUnsigned(NORM_EPILOG_SIZE_ENCBASE);
175
176 m_ValidRangeStart = (UINT32) DENORMALIZE_CODE_OFFSET(normPrologSize);
177 m_ValidRangeEnd = (UINT32) DENORMALIZE_CODE_OFFSET(normCodeLength - normEpilogSize);
178 _ASSERTE(m_ValidRangeStart < m_ValidRangeEnd);
179 }
180 else if (hasSecurityObject || hasGenericsInstContext)
181 {
182 // Decode prolog information
183 UINT32 normPrologSize = (UINT32) m_Reader.DecodeVarLengthUnsigned(NORM_PROLOG_SIZE_ENCBASE) + 1;
184 m_ValidRangeStart = (UINT32) DENORMALIZE_CODE_OFFSET(normPrologSize);
185 // satisfy asserts that assume m_GSCookieValidRangeStart != 0 ==> m_GSCookieValidRangeStart < m_GSCookieValidRangeEnd
186 m_ValidRangeEnd = m_ValidRangeStart + 1;
187 }
188 else
189 {
190 m_ValidRangeStart = m_ValidRangeEnd = 0;
191 }
192
193 if (flags == DECODE_PROLOG_LENGTH) {
194 // Bail, if we've decoded enough,
195 return;
196 }
197
198 // Decode the offset to the security object.
199 if(hasSecurityObject)
200 {
201 m_SecurityObjectStackSlot = (INT32) DENORMALIZE_STACK_SLOT(m_Reader.DecodeVarLengthSigned(SECURITY_OBJECT_STACK_SLOT_ENCBASE));
202 }
203 else
204 {
205 m_SecurityObjectStackSlot = NO_SECURITY_OBJECT;
206 }
207
208 if (flags == DECODE_SECURITY_OBJECT) {
209 // Bail, if we've decoded enough,
210 return;
211 }
212
213 // Decode the offset to the GS cookie.
214 if(hasGSCookie)
215 {
216 m_GSCookieStackSlot = (INT32) DENORMALIZE_STACK_SLOT(m_Reader.DecodeVarLengthSigned(GS_COOKIE_STACK_SLOT_ENCBASE));
217 }
218 else
219 {
220 m_GSCookieStackSlot = NO_GS_COOKIE;
221 }
222
223 if (flags == DECODE_GS_COOKIE) {
224 // Bail, if we've decoded enough,
225 return;
226 }
227
228 // Decode the offset to the PSPSym.
229 // The PSPSym is relative to the caller SP on IA64 and the initial stack pointer before any stack allocation on X64 (InitialSP).
230 if(hasPSPSym)
231 {
232 m_PSPSymStackSlot = (INT32) DENORMALIZE_STACK_SLOT(m_Reader.DecodeVarLengthSigned(PSP_SYM_STACK_SLOT_ENCBASE));
233 }
234 else
235 {
236 m_PSPSymStackSlot = NO_PSP_SYM;
237 }
238
239 if (flags == DECODE_PSP_SYM) {
240 // Bail, if we've decoded enough,
241 return;
242 }
243
244 // Decode the offset to the generics type context.
245 if(hasGenericsInstContext)
246 {
247 m_GenericsInstContextStackSlot = (INT32) DENORMALIZE_STACK_SLOT(m_Reader.DecodeVarLengthSigned(GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE));
248 }
249 else
250 {
251 m_GenericsInstContextStackSlot = NO_GENERICS_INST_CONTEXT;
252 }
253
254 if (flags == DECODE_GENERICS_INST_CONTEXT) {
255 // Bail, if we've decoded enough,
256 return;
257 }
258
259 if(hasStackBaseRegister)
260 {
261 if (slimHeader)
262 {
263 m_StackBaseRegister = (UINT32) DENORMALIZE_STACK_BASE_REGISTER(0);
264 }
265 else
266 {
267 m_StackBaseRegister = (UINT32) DENORMALIZE_STACK_BASE_REGISTER(m_Reader.DecodeVarLengthUnsigned(STACK_BASE_REGISTER_ENCBASE));
268 }
269 }
270 else
271 {
272 m_StackBaseRegister = NO_STACK_BASE_REGISTER;
273 }
274
275 if (hasSizeOfEditAndContinuePreservedArea)
276 {
277 m_SizeOfEditAndContinuePreservedArea = (UINT32) m_Reader.DecodeVarLengthUnsigned(SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE);
278 }
279 else
280 {
281 m_SizeOfEditAndContinuePreservedArea = NO_SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA;
282 }
283
284 if (hasReversePInvokeFrame)
285 {
286 m_ReversePInvokeFrameStackSlot = (INT32)m_Reader.DecodeVarLengthSigned(REVERSE_PINVOKE_FRAME_ENCBASE);
287 }
288 else
289 {
290 m_ReversePInvokeFrameStackSlot = NO_REVERSE_PINVOKE_FRAME;
291 }
292
293
294#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
295 if (slimHeader)
296 {
297 m_SizeOfStackOutgoingAndScratchArea = 0;
298 }
299 else
300 {
301 m_SizeOfStackOutgoingAndScratchArea = (UINT32)DENORMALIZE_SIZE_OF_STACK_AREA(m_Reader.DecodeVarLengthUnsigned(SIZE_OF_STACK_AREA_ENCBASE));
302 }
303#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA
304
305#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
306 m_NumSafePoints = (UINT32) DENORMALIZE_NUM_SAFE_POINTS(m_Reader.DecodeVarLengthUnsigned(NUM_SAFE_POINTS_ENCBASE));
307#endif
308
309 if (slimHeader)
310 {
311 m_NumInterruptibleRanges = 0;
312 }
313 else
314 {
315 m_NumInterruptibleRanges = (UINT32) DENORMALIZE_NUM_INTERRUPTIBLE_RANGES(m_Reader.DecodeVarLengthUnsigned(NUM_INTERRUPTIBLE_RANGES_ENCBASE));
316 }
317
318#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
319 if(flags & (DECODE_INTERRUPTIBILITY | DECODE_GC_LIFETIMES))
320 {
321 if(m_NumSafePoints)
322 {
323 m_SafePointIndex = FindSafePoint(m_InstructionOffset);
324 }
325 else
326 {
327 m_SafePointIndex = 0;
328 }
329 }
330 else if(flags & DECODE_FOR_RANGES_CALLBACK)
331 {
332 // Note that normalization as a code offset can be different than
333 // normalization as code legnth
334 UINT32 normCodeLength = NORMALIZE_CODE_OFFSET(m_CodeLength);
335
336 UINT32 numBitsPerOffset = CeilOfLog2(normCodeLength);
337 m_Reader.Skip(m_NumSafePoints * numBitsPerOffset);
338 }
339#endif
340
341 if(!m_IsInterruptible && (flags & DECODE_INTERRUPTIBILITY))
342 {
343 EnumerateInterruptibleRanges(&SetIsInterruptibleCB, this);
344 }
345}
346
347bool GcInfoDecoder::IsInterruptible()
348{
349 _ASSERTE( m_Flags & DECODE_INTERRUPTIBILITY );
350 return m_IsInterruptible;
351}
352
353bool GcInfoDecoder::HasMethodDescGenericsInstContext()
354{
355 _ASSERTE( m_Flags & DECODE_GENERICS_INST_CONTEXT );
356 return m_GenericSecretParamIsMD;
357}
358
359bool GcInfoDecoder::HasMethodTableGenericsInstContext()
360{
361 _ASSERTE( m_Flags & DECODE_GENERICS_INST_CONTEXT );
362 return m_GenericSecretParamIsMT;
363}
364
365#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
366
367// This is used for gccoverage: is the given offset
368// a call-return offset with partially-interruptible GC info?
369bool GcInfoDecoder::IsSafePoint(UINT32 codeOffset)
370{
371 _ASSERTE(m_Flags == DECODE_EVERYTHING && m_InstructionOffset == 0);
372 if(m_NumSafePoints == 0)
373 return false;
374
375#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
376 // Safepoints are encoded with a -1 adjustment
377 codeOffset--;
378#endif
379 size_t savedPos = m_Reader.GetCurrentPos();
380 UINT32 safePointIndex = FindSafePoint(codeOffset);
381 m_Reader.SetCurrentPos(savedPos);
382 return (bool) (safePointIndex != m_NumSafePoints);
383
384}
385
386UINT32 GcInfoDecoder::FindSafePoint(UINT32 breakOffset)
387{
388 if(m_NumSafePoints == 0)
389 return 0;
390
391 const size_t savedPos = m_Reader.GetCurrentPos();
392 const UINT32 numBitsPerOffset = CeilOfLog2(NORMALIZE_CODE_OFFSET(m_CodeLength));
393 UINT32 result = m_NumSafePoints;
394
395#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
396 // Safepoints are encoded with a -1 adjustment
397 // but normalizing them masks off the low order bit
398 // Thus only bother looking if the address is odd
399 if ((breakOffset & 1) != 0)
400#endif
401 {
402 const UINT32 normBreakOffset = NORMALIZE_CODE_OFFSET(breakOffset);
403
404 INT32 low = 0;
405 INT32 high = (INT32)m_NumSafePoints;
406
407 while(low < high)
408 {
409 const INT32 mid = (low+high)/2;
410 _ASSERTE(mid >= 0 && mid < (INT32)m_NumSafePoints);
411 m_Reader.SetCurrentPos(savedPos + (UINT32)mid * numBitsPerOffset);
412 UINT32 normOffset = (UINT32)m_Reader.Read(numBitsPerOffset);
413 if(normOffset == normBreakOffset)
414 {
415 result = (UINT32) mid;
416 break;
417 }
418
419 if(normOffset < normBreakOffset)
420 low = mid+1;
421 else
422 high = mid;
423 }
424 }
425
426 m_Reader.SetCurrentPos(savedPos + m_NumSafePoints * numBitsPerOffset);
427 return result;
428}
429
430void GcInfoDecoder::EnumerateSafePoints(EnumerateSafePointsCallback *pCallback, void * hCallback)
431{
432 if(m_NumSafePoints == 0)
433 return;
434
435 const UINT32 numBitsPerOffset = CeilOfLog2(NORMALIZE_CODE_OFFSET(m_CodeLength));
436
437 for(UINT32 i = 0; i < m_NumSafePoints; i++)
438 {
439 UINT32 normOffset = (UINT32)m_Reader.Read(numBitsPerOffset);
440 UINT32 offset = DENORMALIZE_CODE_OFFSET(normOffset) + 2;
441
442#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
443 // Safepoints are encoded with a -1 adjustment
444 offset--;
445#endif
446
447 pCallback(offset, hCallback);
448 }
449}
450#endif
451
452void GcInfoDecoder::EnumerateInterruptibleRanges (
453 EnumerateInterruptibleRangesCallback *pCallback,
454 void * hCallback)
455{
456 // If no info is found for the call site, we default to fully-interruptbile
457 LOG((LF_GCROOTS, LL_INFO1000000, "No GC info found for call site at offset %x. Defaulting to fully-interruptible information.\n", (int) m_InstructionOffset));
458
459 UINT32 lastInterruptibleRangeStopOffsetNormalized = 0;
460
461 for(UINT32 i=0; i<m_NumInterruptibleRanges; i++)
462 {
463 UINT32 normStartDelta = (UINT32) m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA1_ENCBASE );
464 UINT32 normStopDelta = (UINT32) m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA2_ENCBASE ) + 1;
465
466 UINT32 rangeStartOffsetNormalized = lastInterruptibleRangeStopOffsetNormalized + normStartDelta;
467 UINT32 rangeStopOffsetNormalized = rangeStartOffsetNormalized + normStopDelta;
468
469 UINT32 rangeStartOffset = DENORMALIZE_CODE_OFFSET(rangeStartOffsetNormalized);
470 UINT32 rangeStopOffset = DENORMALIZE_CODE_OFFSET(rangeStopOffsetNormalized);
471
472 bool fStop = pCallback(rangeStartOffset, rangeStopOffset, hCallback);
473 if (fStop)
474 return;
475
476 lastInterruptibleRangeStopOffsetNormalized = rangeStopOffsetNormalized;
477 }
478}
479
480INT32 GcInfoDecoder::GetSecurityObjectStackSlot()
481{
482 _ASSERTE( m_Flags & DECODE_SECURITY_OBJECT );
483 return m_SecurityObjectStackSlot;
484}
485
486INT32 GcInfoDecoder::GetGSCookieStackSlot()
487{
488 _ASSERTE( m_Flags & DECODE_GS_COOKIE );
489 return m_GSCookieStackSlot;
490}
491
492INT32 GcInfoDecoder::GetReversePInvokeFrameStackSlot()
493{
494 _ASSERTE(m_Flags & DECODE_REVERSE_PINVOKE_VAR);
495 return m_ReversePInvokeFrameStackSlot;
496}
497
498UINT32 GcInfoDecoder::GetGSCookieValidRangeStart()
499{
500 _ASSERTE( m_Flags & DECODE_GS_COOKIE );
501 return m_ValidRangeStart;
502}
503UINT32 GcInfoDecoder::GetGSCookieValidRangeEnd()
504{
505 _ASSERTE( m_Flags & DECODE_GS_COOKIE );
506 return m_ValidRangeEnd;
507}
508
509UINT32 GcInfoDecoder::GetPrologSize()
510{
511 _ASSERTE( m_Flags & DECODE_PROLOG_LENGTH );
512
513 return m_ValidRangeStart;
514}
515
516INT32 GcInfoDecoder::GetGenericsInstContextStackSlot()
517{
518 _ASSERTE( m_Flags & DECODE_GENERICS_INST_CONTEXT );
519 return m_GenericsInstContextStackSlot;
520}
521
522INT32 GcInfoDecoder::GetPSPSymStackSlot()
523{
524 _ASSERTE( m_Flags & DECODE_PSP_SYM );
525 return m_PSPSymStackSlot;
526}
527
528bool GcInfoDecoder::GetIsVarArg()
529{
530 _ASSERTE( m_Flags & DECODE_VARARG );
531 return m_IsVarArg;
532}
533
534#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
535bool GcInfoDecoder::HasTailCalls()
536{
537 _ASSERTE( m_Flags & DECODE_HAS_TAILCALLS );
538 return m_HasTailCalls;
539}
540#endif // _TARGET_ARM_ || _TARGET_ARM64_
541
542bool GcInfoDecoder::WantsReportOnlyLeaf()
543{
544 // Only AMD64 with JIT64 can return false here.
545#ifdef _TARGET_AMD64_
546 return m_WantsReportOnlyLeaf;
547#else
548 return true;
549#endif
550}
551
552UINT32 GcInfoDecoder::GetCodeLength()
553{
554// SUPPORTS_DAC;
555 _ASSERTE( m_Flags & DECODE_CODE_LENGTH );
556 return m_CodeLength;
557}
558
559ReturnKind GcInfoDecoder::GetReturnKind()
560{
561 // SUPPORTS_DAC;
562 _ASSERTE( m_Flags & DECODE_RETURN_KIND );
563 return m_ReturnKind;
564}
565
566UINT32 GcInfoDecoder::GetStackBaseRegister()
567{
568 return m_StackBaseRegister;
569}
570
571UINT32 GcInfoDecoder::GetSizeOfEditAndContinuePreservedArea()
572{
573 _ASSERTE( m_Flags & DECODE_EDIT_AND_CONTINUE );
574 return m_SizeOfEditAndContinuePreservedArea;
575}
576
577size_t GcInfoDecoder::GetNumBytesRead()
578{
579 return (m_Reader.GetCurrentPos() + 7) / 8;
580}
581
582
583#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
584
585UINT32 GcInfoDecoder::GetSizeOfStackParameterArea()
586{
587 return m_SizeOfStackOutgoingAndScratchArea;
588}
589
590#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA
591
592
593bool GcInfoDecoder::EnumerateLiveSlots(
594 PREGDISPLAY pRD,
595 bool reportScratchSlots,
596 unsigned inputFlags,
597 GCEnumCallback pCallBack,
598 void * hCallBack
599 )
600{
601
602 unsigned executionAborted = (inputFlags & ExecutionAborted);
603
604 // In order to make ARM more x86-like we only ever report the leaf frame
605 // of any given function. We accomplish this by having the stackwalker
606 // pass a flag whenever walking the frame of a method where it has
607 // previously visited a child funclet
608 if (WantsReportOnlyLeaf() && (inputFlags & ParentOfFuncletStackFrame))
609 {
610 LOG((LF_GCROOTS, LL_INFO100000, "Not reporting this frame because it was already reported via another funclet.\n"));
611 return true;
612 }
613
614 //
615 // If this is a non-leaf frame and we are executing a call, the unwinder has given us the PC
616 // of the call instruction. We should adjust it to the PC of the instruction after the call in order to
617 // obtain transition information for scratch slots. However, we always assume scratch slots to be
618 // dead for non-leaf frames (except for ResumableFrames), so we don't need to adjust the PC.
619 // If this is a non-leaf frame and we are not executing a call (i.e.: a fault occurred in the function),
620 // then it would be incorrect to adjust the PC
621 //
622
623 _ASSERTE(GC_SLOT_INTERIOR == GC_CALL_INTERIOR);
624 _ASSERTE(GC_SLOT_PINNED == GC_CALL_PINNED);
625
626 _ASSERTE( m_Flags & DECODE_GC_LIFETIMES );
627
628 GcSlotDecoder slotDecoder;
629
630 UINT32 normBreakOffset = NORMALIZE_CODE_OFFSET(m_InstructionOffset);
631
632 // Normalized break offset
633 // Relative to interruptible ranges #if PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
634#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
635 UINT32 pseudoBreakOffset = 0;
636 UINT32 numInterruptibleLength = 0;
637#else
638 UINT32 pseudoBreakOffset = normBreakOffset;
639 UINT32 numInterruptibleLength = NORMALIZE_CODE_OFFSET(m_CodeLength);
640#endif
641
642
643#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
644 bool noTrackedRefs = false;
645
646 if(m_SafePointIndex < m_NumSafePoints && !executionAborted)
647 {
648 // Skip interruptibility information
649 for(UINT32 i=0; i<m_NumInterruptibleRanges; i++)
650 {
651 m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA1_ENCBASE );
652 m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA2_ENCBASE );
653 }
654 }
655 else
656 {
657 //
658 // We didn't find the break offset in the list of call sites
659 // or we are in an executionAborted frame
660 // So either we have fully-interruptible information,
661 // or execution will not resume at the current method
662 // and nothing should be reported
663 //
664 if(!executionAborted)
665 {
666 if(m_NumInterruptibleRanges == 0)
667 {
668 // No ranges and no explicit safepoint - must be MinOpts with untracked refs.
669 noTrackedRefs = true;
670 }
671 }
672
673 if(m_NumInterruptibleRanges != 0)
674 {
675 int countIntersections = 0;
676 UINT32 lastNormStop = 0;
677 for(UINT32 i=0; i<m_NumInterruptibleRanges; i++)
678 {
679 UINT32 normStartDelta = (UINT32) m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA1_ENCBASE );
680 UINT32 normStopDelta = (UINT32) m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA2_ENCBASE ) + 1;
681
682 UINT32 normStart = lastNormStop + normStartDelta;
683 UINT32 normStop = normStart + normStopDelta;
684 if(normBreakOffset >= normStart && normBreakOffset < normStop)
685 {
686 _ASSERTE(pseudoBreakOffset == 0);
687 countIntersections++;
688 pseudoBreakOffset = numInterruptibleLength + normBreakOffset - normStart;
689 }
690 numInterruptibleLength += normStopDelta;
691 lastNormStop = normStop;
692 }
693 _ASSERTE(countIntersections <= 1);
694 if(countIntersections == 0)
695 {
696 _ASSERTE(executionAborted);
697 LOG((LF_GCROOTS, LL_INFO100000, "Not reporting this frame because it is aborted and not fully interruptible.\n"));
698 goto ExitSuccess;
699 }
700 }
701 }
702#else // !PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
703
704 // Skip interruptibility information
705 for(UINT32 i=0; i<m_NumInterruptibleRanges; i++)
706 {
707 m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA1_ENCBASE );
708 m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA2_ENCBASE );
709 }
710#endif
711
712
713 //------------------------------------------------------------------------------
714 // Read the slot table
715 //------------------------------------------------------------------------------
716
717
718 slotDecoder.DecodeSlotTable(m_Reader);
719
720 {
721 UINT32 numSlots = slotDecoder.GetNumTracked();
722
723 if(!numSlots)
724 goto ReportUntracked;
725
726#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
727
728 UINT32 numBitsPerOffset = 0;
729 // Duplicate the encoder's heuristic to determine if we have indirect live
730 // slot table (similar to the chunk pointers)
731 if ((m_NumSafePoints > 0) && m_Reader.ReadOneFast())
732 {
733 numBitsPerOffset = (UINT32) m_Reader.DecodeVarLengthUnsigned(POINTER_SIZE_ENCBASE) + 1;
734 _ASSERTE(numBitsPerOffset != 0);
735 }
736
737 //------------------------------------------------------------------------------
738 // Try partially interruptible first
739 //------------------------------------------------------------------------------
740
741 if( !executionAborted && m_SafePointIndex != m_NumSafePoints )
742 {
743 if (numBitsPerOffset)
744 {
745 const size_t offsetTablePos = m_Reader.GetCurrentPos();
746 m_Reader.Skip(m_SafePointIndex * numBitsPerOffset);
747 const size_t liveStatesOffset = m_Reader.Read(numBitsPerOffset);
748 const size_t liveStatesStart = ((offsetTablePos + m_NumSafePoints * numBitsPerOffset + 7) & (~7));
749 m_Reader.SetCurrentPos(liveStatesStart + liveStatesOffset);
750 if (m_Reader.ReadOneFast()) {
751 // RLE encoded
752 bool fSkip = (m_Reader.ReadOneFast() == 0);
753 bool fReport = true;
754 UINT32 readSlots = (UINT32)m_Reader.DecodeVarLengthUnsigned( fSkip ? LIVESTATE_RLE_SKIP_ENCBASE : LIVESTATE_RLE_RUN_ENCBASE );
755 fSkip = !fSkip;
756 while (readSlots < numSlots)
757 {
758 UINT32 cnt = (UINT32)m_Reader.DecodeVarLengthUnsigned( fSkip ? LIVESTATE_RLE_SKIP_ENCBASE : LIVESTATE_RLE_RUN_ENCBASE ) + 1;
759 if (fReport)
760 {
761 for(UINT32 slotIndex = readSlots; slotIndex < readSlots + cnt; slotIndex++)
762 {
763 ReportSlotToGC(slotDecoder,
764 slotIndex,
765 pRD,
766 reportScratchSlots,
767 inputFlags,
768 pCallBack,
769 hCallBack
770 );
771 }
772 }
773 readSlots += cnt;
774 fSkip = !fSkip;
775 fReport = !fReport;
776 }
777 _ASSERTE(readSlots == numSlots);
778 goto ReportUntracked;
779 }
780 // Just a normal live state (1 bit per slot), so use the normal decoding loop
781 }
782 else
783 {
784 m_Reader.Skip(m_SafePointIndex * numSlots);
785 }
786
787 for(UINT32 slotIndex = 0; slotIndex < numSlots; slotIndex++)
788 {
789 if(m_Reader.ReadOneFast())
790 {
791 ReportSlotToGC(
792 slotDecoder,
793 slotIndex,
794 pRD,
795 reportScratchSlots,
796 inputFlags,
797 pCallBack,
798 hCallBack
799 );
800 }
801 }
802 goto ReportUntracked;
803 }
804 else
805 {
806 m_Reader.Skip(m_NumSafePoints * numSlots);
807 if(m_NumInterruptibleRanges == 0)
808 goto ReportUntracked;
809 }
810#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
811
812 _ASSERTE(m_NumInterruptibleRanges);
813 _ASSERTE(numInterruptibleLength);
814
815 // If no info is found for the call site, we default to fully-interruptbile
816 LOG((LF_GCROOTS, LL_INFO1000000, "No GC info found for call site at offset %x. Defaulting to fully-interruptible information.\n", (int) m_InstructionOffset));
817
818 UINT32 numChunks = (numInterruptibleLength + NUM_NORM_CODE_OFFSETS_PER_CHUNK - 1) / NUM_NORM_CODE_OFFSETS_PER_CHUNK;
819 UINT32 breakChunk = pseudoBreakOffset / NUM_NORM_CODE_OFFSETS_PER_CHUNK;
820 _ASSERTE(breakChunk < numChunks);
821
822 UINT32 numBitsPerPointer = (UINT32) m_Reader.DecodeVarLengthUnsigned(POINTER_SIZE_ENCBASE);
823
824 if(!numBitsPerPointer)
825 goto ReportUntracked;
826
827 size_t pointerTablePos = m_Reader.GetCurrentPos();
828
829 size_t chunkPointer;
830 UINT32 chunk = breakChunk;
831 for(;;)
832 {
833 m_Reader.SetCurrentPos(pointerTablePos + chunk * numBitsPerPointer);
834 chunkPointer = m_Reader.Read(numBitsPerPointer);
835 if(chunkPointer)
836 break;
837
838 if(chunk-- == 0)
839 goto ReportUntracked;
840 }
841
842 size_t chunksStartPos = ((pointerTablePos + numChunks * numBitsPerPointer + 7) & (~7));
843 size_t chunkPos = chunksStartPos + chunkPointer - 1;
844 m_Reader.SetCurrentPos(chunkPos);
845
846 {
847 BitStreamReader couldBeLiveReader(m_Reader);
848
849 UINT32 numCouldBeLiveSlots = 0;
850 // A potentially compressed bit vector of which slots have any lifetimes
851 if (m_Reader.ReadOneFast())
852 {
853 // RLE encoded
854 bool fSkip = (m_Reader.ReadOneFast() == 0);
855 bool fReport = true;
856 UINT32 readSlots = (UINT32)m_Reader.DecodeVarLengthUnsigned( fSkip ? LIVESTATE_RLE_SKIP_ENCBASE : LIVESTATE_RLE_RUN_ENCBASE );
857 fSkip = !fSkip;
858 while (readSlots < numSlots)
859 {
860 UINT32 cnt = (UINT32)m_Reader.DecodeVarLengthUnsigned( fSkip ? LIVESTATE_RLE_SKIP_ENCBASE : LIVESTATE_RLE_RUN_ENCBASE ) + 1;
861 if (fReport)
862 {
863 numCouldBeLiveSlots += cnt;
864 }
865 readSlots += cnt;
866 fSkip = !fSkip;
867 fReport = !fReport;
868 }
869 _ASSERTE(readSlots == numSlots);
870
871 }
872 else
873 {
874 for(UINT32 i = 0; i < numSlots; i++)
875 {
876 if(m_Reader.ReadOneFast())
877 numCouldBeLiveSlots++;
878 }
879 }
880 _ASSERTE(numCouldBeLiveSlots > 0);
881
882 BitStreamReader finalStateReader(m_Reader);
883
884 m_Reader.Skip(numCouldBeLiveSlots);
885
886 int lifetimeTransitionsCount = 0;
887
888 UINT32 slotIndex = 0;
889 bool fSimple = (couldBeLiveReader.ReadOneFast() == 0);
890 bool fSkipFirst = false; // silence the warning
891 UINT32 cnt = 0;
892 if (!fSimple)
893 {
894 fSkipFirst = (couldBeLiveReader.ReadOneFast() == 0);
895 slotIndex = -1;
896 }
897 for(UINT32 i = 0; i < numCouldBeLiveSlots; i++)
898 {
899 if (fSimple)
900 {
901 while(!couldBeLiveReader.ReadOneFast())
902 slotIndex++;
903 }
904 else if (cnt > 0)
905 {
906 // We have more from the last run to report
907 cnt--;
908 }
909 // We need to find a new run
910 else if (fSkipFirst)
911 {
912 UINT32 tmp = (UINT32)couldBeLiveReader.DecodeVarLengthUnsigned( LIVESTATE_RLE_SKIP_ENCBASE ) + 1;
913 slotIndex += tmp;
914 cnt = (UINT32)couldBeLiveReader.DecodeVarLengthUnsigned( LIVESTATE_RLE_RUN_ENCBASE );
915 }
916 else
917 {
918 UINT32 tmp = (UINT32)couldBeLiveReader.DecodeVarLengthUnsigned( LIVESTATE_RLE_RUN_ENCBASE ) + 1;
919 slotIndex += tmp;
920 cnt = (UINT32)couldBeLiveReader.DecodeVarLengthUnsigned( LIVESTATE_RLE_SKIP_ENCBASE );
921 }
922
923 UINT32 isLive = (UINT32) finalStateReader.Read(1);
924
925 if(chunk == breakChunk)
926 {
927 // Read transitions
928 UINT32 normBreakOffsetDelta = pseudoBreakOffset % NUM_NORM_CODE_OFFSETS_PER_CHUNK;
929 for(;;)
930 {
931 if(!m_Reader.ReadOneFast())
932 break;
933
934 UINT32 transitionOffset = (UINT32) m_Reader.Read(NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2);
935
936 lifetimeTransitionsCount++;
937 _ASSERTE(transitionOffset && transitionOffset < NUM_NORM_CODE_OFFSETS_PER_CHUNK);
938 if(transitionOffset > normBreakOffsetDelta)
939 {
940 isLive ^= 1;
941 }
942 }
943 }
944
945 if(isLive)
946 {
947 ReportSlotToGC(
948 slotDecoder,
949 slotIndex,
950 pRD,
951 reportScratchSlots,
952 inputFlags,
953 pCallBack,
954 hCallBack
955 );
956 }
957
958 slotIndex++;
959 }
960
961 LOG((LF_GCROOTS, LL_INFO1000000, "Decoded %d lifetime transitions.\n", (int) lifetimeTransitionsCount ));
962 }
963 }
964
965ReportUntracked:
966
967 //------------------------------------------------------------------------------
968 // Last report anything untracked
969 // But only for the leaf funclet/frame
970 // Turned on in the VM for regular GC reporting and the DAC for !CLRStack -gc
971 // But turned off in the #includes for nidump and sos's !u -gcinfo and !gcinfo
972 //------------------------------------------------------------------------------
973
974 if (slotDecoder.GetNumUntracked() && !(inputFlags & (ParentOfFuncletStackFrame | NoReportUntracked)))
975 {
976 ReportUntrackedSlots(slotDecoder, pRD, inputFlags, pCallBack, hCallBack);
977 }
978
979ExitSuccess:
980
981 return true;
982}
983
984void GcInfoDecoder::EnumerateUntrackedSlots(
985 PREGDISPLAY pRD,
986 unsigned inputFlags,
987 GCEnumCallback pCallBack,
988 void * hCallBack
989 )
990{
991 _ASSERTE(GC_SLOT_INTERIOR == GC_CALL_INTERIOR);
992 _ASSERTE(GC_SLOT_PINNED == GC_CALL_PINNED);
993
994 _ASSERTE( m_Flags & DECODE_GC_LIFETIMES );
995
996 GcSlotDecoder slotDecoder;
997
998 // Skip interruptibility information
999 for(UINT32 i=0; i<m_NumInterruptibleRanges; i++)
1000 {
1001 m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA1_ENCBASE );
1002 m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA2_ENCBASE );
1003 }
1004
1005 //------------------------------------------------------------------------------
1006 // Read the slot table
1007 //------------------------------------------------------------------------------
1008
1009 slotDecoder.DecodeSlotTable(m_Reader);
1010
1011 if (slotDecoder.GetNumUntracked())
1012 {
1013 ReportUntrackedSlots(slotDecoder, pRD, inputFlags, pCallBack, hCallBack);
1014 }
1015}
1016
1017void GcInfoDecoder::ReportUntrackedSlots(
1018 GcSlotDecoder& slotDecoder,
1019 PREGDISPLAY pRD,
1020 unsigned inputFlags,
1021 GCEnumCallback pCallBack,
1022 void * hCallBack
1023 )
1024{
1025 for(UINT32 slotIndex = slotDecoder.GetNumTracked(); slotIndex < slotDecoder.GetNumSlots(); slotIndex++)
1026 {
1027 ReportSlotToGC(slotDecoder,
1028 slotIndex,
1029 pRD,
1030 true, // Report everything (although there should *never* be any scratch slots that are untracked)
1031 inputFlags,
1032 pCallBack,
1033 hCallBack
1034 );
1035 }
1036}
1037
1038void GcSlotDecoder::DecodeSlotTable(BitStreamReader& reader)
1039{
1040 if (reader.ReadOneFast())
1041 {
1042 m_NumRegisters = (UINT32) reader.DecodeVarLengthUnsigned(NUM_REGISTERS_ENCBASE);
1043 }
1044 else
1045 {
1046 m_NumRegisters = 0;
1047 }
1048 UINT32 numStackSlots;
1049 if (reader.ReadOneFast())
1050 {
1051 numStackSlots = (UINT32) reader.DecodeVarLengthUnsigned(NUM_STACK_SLOTS_ENCBASE);
1052 m_NumUntracked = (UINT32) reader.DecodeVarLengthUnsigned(NUM_UNTRACKED_SLOTS_ENCBASE);
1053 }
1054 else
1055 {
1056 numStackSlots = 0;
1057 m_NumUntracked = 0;
1058 }
1059 m_NumSlots = m_NumRegisters + numStackSlots + m_NumUntracked;
1060
1061 UINT32 i = 0;
1062
1063 if(m_NumRegisters > 0)
1064 {
1065 // We certainly predecode the first register
1066
1067 _ASSERTE(i < MAX_PREDECODED_SLOTS);
1068
1069 UINT32 normRegNum = (UINT32) reader.DecodeVarLengthUnsigned(REGISTER_ENCBASE);
1070 UINT32 regNum = DENORMALIZE_REGISTER(normRegNum);
1071 GcSlotFlags flags = (GcSlotFlags) reader.Read(2);
1072
1073 m_SlotArray[0].Slot.RegisterNumber = regNum;
1074 m_SlotArray[0].Flags = flags;
1075
1076 UINT32 loopEnd = _min(m_NumRegisters, MAX_PREDECODED_SLOTS);
1077 for(i++; i < loopEnd; i++)
1078 {
1079 if(flags)
1080 {
1081 normRegNum = (UINT32) reader.DecodeVarLengthUnsigned(REGISTER_ENCBASE);
1082 regNum = DENORMALIZE_REGISTER(normRegNum);
1083 flags = (GcSlotFlags) reader.Read(2);
1084 }
1085 else
1086 {
1087 UINT32 normRegDelta = (UINT32) reader.DecodeVarLengthUnsigned(REGISTER_DELTA_ENCBASE) + 1;
1088 normRegNum += normRegDelta;
1089 regNum = DENORMALIZE_REGISTER(normRegNum);
1090 }
1091
1092 m_SlotArray[i].Slot.RegisterNumber = regNum;
1093 m_SlotArray[i].Flags = flags;
1094 }
1095 }
1096
1097 if((numStackSlots > 0) && (i < MAX_PREDECODED_SLOTS))
1098 {
1099 // We have stack slots left and more room to predecode
1100
1101 GcStackSlotBase spBase = (GcStackSlotBase) reader.Read(2);
1102 UINT32 normSpOffset = (INT32) reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE);
1103 INT32 spOffset = DENORMALIZE_STACK_SLOT(normSpOffset);
1104 GcSlotFlags flags = (GcSlotFlags) reader.Read(2);
1105
1106 m_SlotArray[i].Slot.Stack.SpOffset = spOffset;
1107 m_SlotArray[i].Slot.Stack.Base = spBase;
1108 m_SlotArray[i].Flags = flags;
1109
1110 UINT32 loopEnd = _min(m_NumRegisters + numStackSlots, MAX_PREDECODED_SLOTS);
1111 for(i++; i < loopEnd; i++)
1112 {
1113 spBase = (GcStackSlotBase) reader.Read(2);
1114
1115 if(flags)
1116 {
1117 normSpOffset = (INT32) reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE);
1118 spOffset = DENORMALIZE_STACK_SLOT(normSpOffset);
1119 flags = (GcSlotFlags) reader.Read(2);
1120 }
1121 else
1122 {
1123 INT32 normSpOffsetDelta = (INT32) reader.DecodeVarLengthUnsigned(STACK_SLOT_DELTA_ENCBASE);
1124 normSpOffset += normSpOffsetDelta;
1125 spOffset = DENORMALIZE_STACK_SLOT(normSpOffset);
1126 }
1127
1128 m_SlotArray[i].Slot.Stack.SpOffset = spOffset;
1129 m_SlotArray[i].Slot.Stack.Base = spBase;
1130 m_SlotArray[i].Flags = flags;
1131 }
1132 }
1133
1134 if((m_NumUntracked > 0) && (i < MAX_PREDECODED_SLOTS))
1135 {
1136 // We have untracked stack slots left and more room to predecode
1137
1138 GcStackSlotBase spBase = (GcStackSlotBase) reader.Read(2);
1139 UINT32 normSpOffset = (INT32) reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE);
1140 INT32 spOffset = DENORMALIZE_STACK_SLOT(normSpOffset);
1141 GcSlotFlags flags = (GcSlotFlags) reader.Read(2);
1142
1143 m_SlotArray[i].Slot.Stack.SpOffset = spOffset;
1144 m_SlotArray[i].Slot.Stack.Base = spBase;
1145 m_SlotArray[i].Flags = flags;
1146
1147 UINT32 loopEnd = _min(m_NumSlots, MAX_PREDECODED_SLOTS);
1148 for(i++; i < loopEnd; i++)
1149 {
1150 spBase = (GcStackSlotBase) reader.Read(2);
1151
1152 if(flags)
1153 {
1154 normSpOffset = (INT32) reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE);
1155 spOffset = DENORMALIZE_STACK_SLOT(normSpOffset);
1156 flags = (GcSlotFlags) reader.Read(2);
1157 }
1158 else
1159 {
1160 INT32 normSpOffsetDelta = (INT32) reader.DecodeVarLengthUnsigned(STACK_SLOT_DELTA_ENCBASE);
1161 normSpOffset += normSpOffsetDelta;
1162 spOffset = DENORMALIZE_STACK_SLOT(normSpOffset);
1163 }
1164
1165 m_SlotArray[i].Slot.Stack.SpOffset = spOffset;
1166 m_SlotArray[i].Slot.Stack.Base = spBase;
1167 m_SlotArray[i].Flags = flags;
1168 }
1169 }
1170
1171 // Done pre-decoding
1172
1173 if(i < m_NumSlots)
1174 {
1175 // Prepare for lazy decoding
1176
1177 _ASSERTE(i == MAX_PREDECODED_SLOTS);
1178 m_NumDecodedSlots = i;
1179 m_pLastSlot = &m_SlotArray[MAX_PREDECODED_SLOTS - 1];
1180
1181 m_SlotReader = reader;
1182
1183 // Move the argument reader past the end of the table
1184
1185 GcSlotFlags flags = m_pLastSlot->Flags;
1186
1187 // Skip any remaining registers
1188
1189 for(; i < m_NumRegisters; i++)
1190 {
1191 if(flags)
1192 {
1193 reader.DecodeVarLengthUnsigned(REGISTER_ENCBASE);
1194 flags = (GcSlotFlags) reader.Read(2);
1195 }
1196 else
1197 {
1198 reader.DecodeVarLengthUnsigned(REGISTER_DELTA_ENCBASE);
1199 }
1200 }
1201
1202 if(numStackSlots > 0)
1203 {
1204 if(i == m_NumRegisters)
1205 {
1206 // Skip the first stack slot
1207
1208 reader.Read(2);
1209 reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE);
1210 flags = (GcSlotFlags) reader.Read(2);
1211 i++;
1212 }
1213
1214 // Skip any remaining stack slots
1215
1216 const UINT32 loopEnd = m_NumRegisters + numStackSlots;
1217 for(; i < loopEnd; i++)
1218 {
1219 reader.Read(2);
1220
1221 if(flags)
1222 {
1223 reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE);
1224 flags = (GcSlotFlags) reader.Read(2);
1225 }
1226 else
1227 {
1228 reader.DecodeVarLengthUnsigned(STACK_SLOT_DELTA_ENCBASE);
1229 }
1230 }
1231 }
1232
1233 if(m_NumUntracked > 0)
1234 {
1235 if(i == m_NumRegisters + numStackSlots)
1236 {
1237 // Skip the first untracked slot
1238
1239 reader.Read(2);
1240 reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE);
1241 flags = (GcSlotFlags) reader.Read(2);
1242 i++;
1243 }
1244
1245 // Skip any remaining untracked slots
1246
1247 for(; i < m_NumSlots; i++)
1248 {
1249 reader.Read(2);
1250
1251 if(flags)
1252 {
1253 reader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE);
1254 flags = (GcSlotFlags) reader.Read(2);
1255 }
1256 else
1257 {
1258 reader.DecodeVarLengthUnsigned(STACK_SLOT_DELTA_ENCBASE);
1259 }
1260 }
1261 }
1262 }
1263}
1264
1265const GcSlotDesc* GcSlotDecoder::GetSlotDesc(UINT32 slotIndex)
1266{
1267 _ASSERTE(slotIndex < m_NumSlots);
1268
1269 if(slotIndex < MAX_PREDECODED_SLOTS)
1270 {
1271 return &m_SlotArray[slotIndex];
1272 }
1273
1274 _ASSERTE(m_NumDecodedSlots >= MAX_PREDECODED_SLOTS && m_NumDecodedSlots < m_NumSlots);
1275 _ASSERTE(m_NumDecodedSlots <= slotIndex);
1276
1277 while(m_NumDecodedSlots <= slotIndex)
1278 {
1279 if(m_NumDecodedSlots < m_NumRegisters)
1280 {
1281 //
1282 // Decode a register
1283 //
1284
1285 if(m_NumDecodedSlots == 0)
1286 {
1287 // Decode the first register
1288 UINT32 normRegNum = (UINT32) m_SlotReader.DecodeVarLengthUnsigned(REGISTER_ENCBASE);
1289 m_pLastSlot->Slot.RegisterNumber = DENORMALIZE_REGISTER(normRegNum);
1290 m_pLastSlot->Flags = (GcSlotFlags) m_SlotReader.Read(2);
1291 }
1292 else
1293 {
1294 if(m_pLastSlot->Flags)
1295 {
1296 UINT32 normRegNum = (UINT32) m_SlotReader.DecodeVarLengthUnsigned(REGISTER_ENCBASE);
1297 m_pLastSlot->Slot.RegisterNumber = DENORMALIZE_REGISTER(normRegNum);
1298 m_pLastSlot->Flags = (GcSlotFlags) m_SlotReader.Read(2);
1299 }
1300 else
1301 {
1302 UINT32 normRegDelta = (UINT32) m_SlotReader.DecodeVarLengthUnsigned(REGISTER_DELTA_ENCBASE) + 1;
1303 UINT32 normRegNum = normRegDelta + NORMALIZE_REGISTER(m_pLastSlot->Slot.RegisterNumber);
1304 m_pLastSlot->Slot.RegisterNumber = DENORMALIZE_REGISTER(normRegNum);
1305 }
1306 }
1307 }
1308 else
1309 {
1310 //
1311 // Decode a stack slot
1312 //
1313
1314 if((m_NumDecodedSlots == m_NumRegisters) || (m_NumDecodedSlots == GetNumTracked()))
1315 {
1316 // Decode the first stack slot or first untracked slot
1317 m_pLastSlot->Slot.Stack.Base = (GcStackSlotBase) m_SlotReader.Read(2);
1318 UINT32 normSpOffset = (INT32) m_SlotReader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE);
1319 m_pLastSlot->Slot.Stack.SpOffset = DENORMALIZE_STACK_SLOT(normSpOffset);
1320 m_pLastSlot->Flags = (GcSlotFlags) m_SlotReader.Read(2);
1321 }
1322 else
1323 {
1324 m_pLastSlot->Slot.Stack.Base = (GcStackSlotBase) m_SlotReader.Read(2);
1325
1326 if(m_pLastSlot->Flags)
1327 {
1328 INT32 normSpOffset = (INT32) m_SlotReader.DecodeVarLengthSigned(STACK_SLOT_ENCBASE);
1329 m_pLastSlot->Slot.Stack.SpOffset = DENORMALIZE_STACK_SLOT(normSpOffset);
1330 m_pLastSlot->Flags = (GcSlotFlags) m_SlotReader.Read(2);
1331 }
1332 else
1333 {
1334 INT32 normSpOffsetDelta = (INT32) m_SlotReader.DecodeVarLengthUnsigned(STACK_SLOT_DELTA_ENCBASE);
1335 INT32 normSpOffset = normSpOffsetDelta + NORMALIZE_STACK_SLOT(m_pLastSlot->Slot.Stack.SpOffset);
1336 m_pLastSlot->Slot.Stack.SpOffset = DENORMALIZE_STACK_SLOT(normSpOffset);
1337 }
1338 }
1339 }
1340
1341 m_NumDecodedSlots++;
1342 }
1343
1344 return m_pLastSlot;
1345}
1346
1347
1348//-----------------------------------------------------------------------------
1349// Platform-specific methods
1350//-----------------------------------------------------------------------------
1351
1352#if defined(_TARGET_AMD64_)
1353
1354
1355OBJECTREF* GcInfoDecoder::GetRegisterSlot(
1356 int regNum,
1357 PREGDISPLAY pRD
1358 )
1359{
1360 _ASSERTE(regNum >= 0 && regNum <= 16);
1361 _ASSERTE(regNum != 4); // rsp
1362
1363#ifdef FEATURE_REDHAWK
1364 PTR_UIntNative* ppRax = &pRD->pRax;
1365 if (regNum > 4) regNum--; // rsp is skipped in Redhawk RegDisplay
1366#else
1367 // The fields of KNONVOLATILE_CONTEXT_POINTERS are in the same order as
1368 // the processor encoding numbers.
1369
1370 ULONGLONG **ppRax = &pRD->pCurrentContextPointers->Rax;
1371#endif
1372
1373 return (OBJECTREF*)*(ppRax + regNum);
1374}
1375
1376#ifdef FEATURE_PAL
1377OBJECTREF* GcInfoDecoder::GetCapturedRegister(
1378 int regNum,
1379 PREGDISPLAY pRD
1380 )
1381{
1382 _ASSERTE(regNum >= 0 && regNum <= 16);
1383 _ASSERTE(regNum != 4); // rsp
1384
1385 // The fields of CONTEXT are in the same order as
1386 // the processor encoding numbers.
1387
1388 ULONGLONG *pRax = &pRD->pCurrentContext->Rax;
1389
1390 return (OBJECTREF*)(pRax + regNum);
1391}
1392#endif // FEATURE_PAL
1393
1394bool GcInfoDecoder::IsScratchRegister(int regNum, PREGDISPLAY pRD)
1395{
1396 _ASSERTE(regNum >= 0 && regNum <= 16);
1397 _ASSERTE(regNum != 4); // rsp
1398
1399 UINT16 PreservedRegMask =
1400 (1 << 3) // rbx
1401 | (1 << 5) // rbp
1402#ifndef UNIX_AMD64_ABI
1403 | (1 << 6) // rsi
1404 | (1 << 7) // rdi
1405#endif // UNIX_AMD64_ABI
1406 | (1 << 12) // r12
1407 | (1 << 13) // r13
1408 | (1 << 14) // r14
1409 | (1 << 15); // r15
1410
1411 return !(PreservedRegMask & (1 << regNum));
1412}
1413
1414
1415bool GcInfoDecoder::IsScratchStackSlot(INT32 spOffset, GcStackSlotBase spBase, PREGDISPLAY pRD)
1416{
1417#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
1418 _ASSERTE( m_Flags & DECODE_GC_LIFETIMES );
1419
1420 TADDR pSlot = (TADDR) GetStackSlot(spOffset, spBase, pRD);
1421 _ASSERTE(pSlot >= pRD->SP);
1422
1423 return (pSlot < pRD->SP + m_SizeOfStackOutgoingAndScratchArea);
1424#else
1425 return FALSE;
1426#endif
1427}
1428
1429
1430void GcInfoDecoder::ReportRegisterToGC( // AMD64
1431 int regNum,
1432 unsigned gcFlags,
1433 PREGDISPLAY pRD,
1434 unsigned flags,
1435 GCEnumCallback pCallBack,
1436 void * hCallBack)
1437{
1438 GCINFODECODER_CONTRACT;
1439
1440 _ASSERTE(regNum >= 0 && regNum <= 16);
1441 _ASSERTE(regNum != 4); // rsp
1442
1443 LOG((LF_GCROOTS, LL_INFO1000, "Reporting " FMT_REG, regNum ));
1444
1445 OBJECTREF* pObjRef = GetRegisterSlot( regNum, pRD );
1446#if defined(FEATURE_PAL) && !defined(SOS_TARGET_AMD64)
1447 // On PAL, we don't always have the context pointers available due to
1448 // a limitation of an unwinding library. In such case, the context
1449 // pointers for some nonvolatile registers are NULL.
1450 // In such case, we let the pObjRef point to the captured register
1451 // value in the context and pin the object itself.
1452 if (pObjRef == NULL)
1453 {
1454 // Report a pinned object to GC only in the promotion phase when the
1455 // GC is scanning roots.
1456 GCCONTEXT* pGCCtx = (GCCONTEXT*)(hCallBack);
1457 if (!pGCCtx->sc->promotion)
1458 {
1459 return;
1460 }
1461
1462 pObjRef = GetCapturedRegister(regNum, pRD);
1463
1464 gcFlags |= GC_CALL_PINNED;
1465 }
1466#endif // FEATURE_PAL && !SOS_TARGET_AMD64
1467
1468#ifdef _DEBUG
1469 if(IsScratchRegister(regNum, pRD))
1470 {
1471 // Scratch registers cannot be reported for non-leaf frames
1472 _ASSERTE(flags & ActiveStackFrame);
1473 }
1474
1475 LOG((LF_GCROOTS, LL_INFO1000, /* Part Two */
1476 "at" FMT_ADDR "as ", DBG_ADDR(pObjRef) ));
1477
1478 VALIDATE_ROOT((gcFlags & GC_CALL_INTERIOR), hCallBack, pObjRef);
1479
1480 LOG_PIPTR(pObjRef, gcFlags, hCallBack);
1481#endif //_DEBUG
1482
1483 gcFlags |= CHECK_APP_DOMAIN;
1484
1485 pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(regNum, 0, false)));
1486}
1487
1488#elif defined(_TARGET_ARM_)
1489
1490OBJECTREF* GcInfoDecoder::GetRegisterSlot(
1491 int regNum,
1492 PREGDISPLAY pRD
1493 )
1494{
1495 _ASSERTE(regNum >= 0 && regNum <= 14);
1496 _ASSERTE(regNum != 13); // sp
1497
1498 DWORD **ppReg;
1499
1500 if(regNum <= 3)
1501 {
1502 ppReg = &pRD->volatileCurrContextPointers.R0;
1503 return (OBJECTREF*)*(ppReg + regNum);
1504 }
1505 else if(regNum == 12)
1506 {
1507 return (OBJECTREF*) pRD->volatileCurrContextPointers.R12;
1508 }
1509 else if(regNum == 14)
1510 {
1511 return (OBJECTREF*) pRD->pCurrentContextPointers->Lr;
1512 }
1513
1514 ppReg = &pRD->pCurrentContextPointers->R4;
1515
1516 return (OBJECTREF*)*(ppReg + regNum-4);
1517
1518}
1519
1520#ifdef FEATURE_PAL
1521OBJECTREF* GcInfoDecoder::GetCapturedRegister(
1522 int regNum,
1523 PREGDISPLAY pRD
1524 )
1525{
1526 _ASSERTE(regNum >= 0 && regNum <= 14);
1527 _ASSERTE(regNum != 13); // sp
1528
1529 // The fields of CONTEXT are in the same order as
1530 // the processor encoding numbers.
1531
1532 ULONG *pR0 = &pRD->pCurrentContext->R0;
1533
1534 return (OBJECTREF*)(pR0 + regNum);
1535}
1536#endif // FEATURE_PAL
1537
1538
1539bool GcInfoDecoder::IsScratchRegister(int regNum, PREGDISPLAY pRD)
1540{
1541 _ASSERTE(regNum >= 0 && regNum <= 14);
1542 _ASSERTE(regNum != 13); // sp
1543
1544 return regNum <= 3 || regNum >= 12; // R12 and R14/LR are both scratch registers
1545}
1546
1547
1548bool GcInfoDecoder::IsScratchStackSlot(INT32 spOffset, GcStackSlotBase spBase, PREGDISPLAY pRD)
1549{
1550#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
1551 _ASSERTE( m_Flags & DECODE_GC_LIFETIMES );
1552
1553 TADDR pSlot = (TADDR) GetStackSlot(spOffset, spBase, pRD);
1554 _ASSERTE(pSlot >= pRD->SP);
1555
1556 return (pSlot < pRD->SP + m_SizeOfStackOutgoingAndScratchArea);
1557#else
1558 return FALSE;
1559#endif
1560}
1561
1562
1563void GcInfoDecoder::ReportRegisterToGC( // ARM
1564 int regNum,
1565 unsigned gcFlags,
1566 PREGDISPLAY pRD,
1567 unsigned flags,
1568 GCEnumCallback pCallBack,
1569 void * hCallBack)
1570{
1571 GCINFODECODER_CONTRACT;
1572
1573 _ASSERTE(regNum >= 0 && regNum <= 14);
1574 _ASSERTE(regNum != 13); // sp
1575
1576 LOG((LF_GCROOTS, LL_INFO1000, "Reporting " FMT_REG, regNum ));
1577
1578 OBJECTREF* pObjRef = GetRegisterSlot( regNum, pRD );
1579
1580#ifdef _DEBUG
1581 if(IsScratchRegister(regNum, pRD))
1582 {
1583 // Scratch registers cannot be reported for non-leaf frames
1584 _ASSERTE(flags & ActiveStackFrame);
1585 }
1586
1587 LOG((LF_GCROOTS, LL_INFO1000, /* Part Two */
1588 "at" FMT_ADDR "as ", DBG_ADDR(pObjRef) ));
1589
1590 VALIDATE_ROOT((gcFlags & GC_CALL_INTERIOR), hCallBack, pObjRef);
1591
1592 LOG_PIPTR(pObjRef, gcFlags, hCallBack);
1593#endif //_DEBUG
1594
1595 gcFlags |= CHECK_APP_DOMAIN;
1596
1597 pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(regNum, 0, false)));
1598}
1599
1600#elif defined(_TARGET_ARM64_)
1601
1602OBJECTREF* GcInfoDecoder::GetRegisterSlot(
1603 int regNum,
1604 PREGDISPLAY pRD
1605 )
1606{
1607 _ASSERTE(regNum >= 0 && regNum <= 30);
1608 _ASSERTE(regNum != 18); // TEB
1609
1610 DWORD64 **ppReg;
1611
1612 if(regNum <= 17)
1613 {
1614 ppReg = &pRD->volatileCurrContextPointers.X0;
1615 return (OBJECTREF*)*(ppReg + regNum);
1616 }
1617 else if(regNum == 29)
1618 {
1619 return (OBJECTREF*) pRD->pCurrentContextPointers->Fp;
1620 }
1621 else if(regNum == 30)
1622 {
1623 return (OBJECTREF*) pRD->pCurrentContextPointers->Lr;
1624 }
1625
1626 ppReg = &pRD->pCurrentContextPointers->X19;
1627
1628 return (OBJECTREF*)*(ppReg + regNum-19);
1629}
1630
1631bool GcInfoDecoder::IsScratchRegister(int regNum, PREGDISPLAY pRD)
1632{
1633 _ASSERTE(regNum >= 0 && regNum <= 30);
1634 _ASSERTE(regNum != 18);
1635
1636 return regNum <= 17 || regNum >= 29; // R12 and R14/LR are both scratch registers
1637}
1638
1639bool GcInfoDecoder::IsScratchStackSlot(INT32 spOffset, GcStackSlotBase spBase, PREGDISPLAY pRD)
1640{
1641#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
1642 _ASSERTE( m_Flags & DECODE_GC_LIFETIMES );
1643
1644 TADDR pSlot = (TADDR) GetStackSlot(spOffset, spBase, pRD);
1645 _ASSERTE(pSlot >= pRD->SP);
1646
1647 return (pSlot < pRD->SP + m_SizeOfStackOutgoingAndScratchArea);
1648#else
1649 return FALSE;
1650#endif
1651
1652}
1653
1654void GcInfoDecoder::ReportRegisterToGC( // ARM64
1655 int regNum,
1656 unsigned gcFlags,
1657 PREGDISPLAY pRD,
1658 unsigned flags,
1659 GCEnumCallback pCallBack,
1660 void * hCallBack)
1661{
1662 GCINFODECODER_CONTRACT;
1663
1664 _ASSERTE(regNum >= 0 && regNum <= 30);
1665 _ASSERTE(regNum != 18);
1666
1667 LOG((LF_GCROOTS, LL_INFO1000, "Reporting " FMT_REG, regNum ));
1668
1669 OBJECTREF* pObjRef = GetRegisterSlot( regNum, pRD );
1670
1671#ifdef _DEBUG
1672 if(IsScratchRegister(regNum, pRD))
1673 {
1674 // Scratch registers cannot be reported for non-leaf frames
1675 _ASSERTE(flags & ActiveStackFrame);
1676 }
1677
1678 LOG((LF_GCROOTS, LL_INFO1000, /* Part Two */
1679 "at" FMT_ADDR "as ", DBG_ADDR(pObjRef) ));
1680
1681 VALIDATE_ROOT((gcFlags & GC_CALL_INTERIOR), hCallBack, pObjRef);
1682
1683 LOG_PIPTR(pObjRef, gcFlags, hCallBack);
1684#endif //_DEBUG
1685
1686 gcFlags |= CHECK_APP_DOMAIN;
1687
1688 pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(regNum, 0, false)));
1689}
1690
1691#ifdef FEATURE_PAL
1692OBJECTREF* GcInfoDecoder::GetCapturedRegister(
1693 int regNum,
1694 PREGDISPLAY pRD
1695 )
1696{
1697 _ASSERTE(regNum >= 0 && regNum <= 28);
1698
1699 // The fields of CONTEXT are in the same order as
1700 // the processor encoding numbers.
1701
1702 DWORD64 *pX0 = &pRD->pCurrentContext->X0;
1703
1704 return (OBJECTREF*)(pX0 + regNum);
1705}
1706#endif // FEATURE_PAL
1707
1708#else // Unknown platform
1709
1710OBJECTREF* GcInfoDecoder::GetRegisterSlot(
1711 int regNum,
1712 PREGDISPLAY pRD
1713 )
1714{
1715 PORTABILITY_ASSERT("GcInfoDecoder::GetRegisterSlot");
1716 return NULL;
1717}
1718
1719bool GcInfoDecoder::IsScratchRegister(int regNum, PREGDISPLAY pRD)
1720{
1721 PORTABILITY_ASSERT("GcInfoDecoder::IsScratchRegister");
1722 return false;
1723}
1724
1725bool GcInfoDecoder::IsScratchStackSlot(INT32 spOffset, GcStackSlotBase spBase, PREGDISPLAY pRD)
1726{
1727 _ASSERTE( !"NYI" );
1728 return false;
1729}
1730
1731void GcInfoDecoder::ReportRegisterToGC(
1732 int regNum,
1733 unsigned gcFlags,
1734 PREGDISPLAY pRD,
1735 unsigned flags,
1736 GCEnumCallback pCallBack,
1737 void * hCallBack)
1738{
1739 _ASSERTE( !"NYI" );
1740}
1741
1742#endif // Unknown platform
1743
1744
1745OBJECTREF* GcInfoDecoder::GetStackSlot(
1746 INT32 spOffset,
1747 GcStackSlotBase spBase,
1748 PREGDISPLAY pRD
1749 )
1750{
1751#ifdef CROSSGEN_COMPILE
1752 _ASSERTE(!"GcInfoDecoder::GetStackSlot not supported in this build configuration");
1753 return NULL;
1754#else // CROSSGEN_COMPILE
1755 OBJECTREF* pObjRef;
1756
1757 if( GC_SP_REL == spBase )
1758 {
1759 pObjRef = (OBJECTREF*) ((SIZE_T)pRD->SP + spOffset);
1760 }
1761 else if( GC_CALLER_SP_REL == spBase )
1762 {
1763 pObjRef = (OBJECTREF*) (GET_CALLER_SP(pRD) + spOffset);
1764 }
1765 else
1766 {
1767 _ASSERTE( GC_FRAMEREG_REL == spBase );
1768 _ASSERTE( NO_STACK_BASE_REGISTER != m_StackBaseRegister );
1769
1770 SIZE_T * pFrameReg = (SIZE_T*) GetRegisterSlot(m_StackBaseRegister, pRD);
1771
1772#ifdef FEATURE_PAL
1773 // On PAL, we don't always have the context pointers available due to
1774 // a limitation of an unwinding library. In such case, the context
1775 // pointers for some nonvolatile registers are NULL.
1776 if (pFrameReg == NULL)
1777 {
1778 pFrameReg = (SIZE_T*) GetCapturedRegister(m_StackBaseRegister, pRD);
1779 }
1780#endif // FEATURE_PAL
1781
1782 pObjRef = (OBJECTREF*)(*pFrameReg + spOffset);
1783 }
1784
1785 return pObjRef;
1786#endif // CROSSGEN_COMPILE
1787}
1788
1789#ifdef DACCESS_COMPILE
1790int GcInfoDecoder::GetStackReg(int spBase)
1791{
1792#if defined(_TARGET_AMD64_)
1793 int esp = 4;
1794#elif defined(_TARGET_ARM_)
1795 int esp = 13;
1796#elif defined(_TARGET_ARM64_)
1797 int esp = 31;
1798#endif
1799
1800 if( GC_SP_REL == spBase )
1801 return esp;
1802 else if ( GC_CALLER_SP_REL == spBase )
1803 return -(esp+1);
1804 else
1805 return m_StackBaseRegister;
1806}
1807#endif // DACCESS_COMPILE
1808
1809void GcInfoDecoder::ReportStackSlotToGC(
1810 INT32 spOffset,
1811 GcStackSlotBase spBase,
1812 unsigned gcFlags,
1813 PREGDISPLAY pRD,
1814 unsigned flags,
1815 GCEnumCallback pCallBack,
1816 void * hCallBack)
1817{
1818 GCINFODECODER_CONTRACT;
1819
1820 OBJECTREF* pObjRef = GetStackSlot(spOffset, spBase, pRD);
1821 _ASSERTE( IS_ALIGNED( pObjRef, sizeof( Object* ) ) );
1822
1823#ifdef _DEBUG
1824 LOG((LF_GCROOTS, LL_INFO1000, /* Part One */
1825 "Reporting %s" FMT_STK,
1826 ( (GC_SP_REL == spBase) ? "" :
1827 ((GC_CALLER_SP_REL == spBase) ? "caller's " :
1828 ((GC_FRAMEREG_REL == spBase) ? "frame " : "<unrecognized GcStackSlotBase> "))),
1829 DBG_STK(spOffset) ));
1830
1831 LOG((LF_GCROOTS, LL_INFO1000, /* Part Two */
1832 "at" FMT_ADDR "as ", DBG_ADDR(pObjRef) ));
1833
1834 VALIDATE_ROOT((gcFlags & GC_CALL_INTERIOR), hCallBack, pObjRef);
1835
1836 LOG_PIPTR(pObjRef, gcFlags, hCallBack);
1837#endif
1838
1839 gcFlags |= CHECK_APP_DOMAIN;
1840
1841 pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(GetStackReg(spBase), spOffset, true)));
1842}
1843
1844
1845#endif // USE_GC_INFO_DECODER
1846
1847