// Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize);
+ // We keep the call count for the second call to gcMakeRegPtrTable() below.
+ unsigned callCnt = 0;
// First we figure out the encoder ID's for the stack slots and registers.
- gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS);
+ gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt);
// Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them).
gcInfoEncoder->FinalizeSlotIds();
// Now we can actually use those slot ID's to declare live ranges.
- gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK);
+ gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt);
gcInfoEncoder->Build();
// Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize);
+ // We keep the call count for the second call to gcMakeRegPtrTable() below.
+ unsigned callCnt = 0;
+
// First we figure out the encoder ID's for the stack slots and registers.
- gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS);
+ gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt);
// Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them).
gcInfoEncoder->FinalizeSlotIds();
// Now we can actually use those slot ID's to declare live ranges.
- gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK);
+ gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt);
if (compiler->opts.compDbgEnC)
{
// Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize);
+ // We keep the call count for the second call to gcMakeRegPtrTable() below.
+ unsigned callCnt = 0;
// First we figure out the encoder ID's for the stack slots and registers.
- gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS);
+ gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt);
// Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them).
gcInfoEncoder->FinalizeSlotIds();
// Now we can actually use those slot ID's to declare live ranges.
- gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK);
+ gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt);
gcInfoEncoder->Build();
// Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize);
+ // We keep the call count for the second call to gcMakeRegPtrTable() below.
+ unsigned callCnt = 0;
// First we figure out the encoder ID's for the stack slots and registers.
- gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS);
+ gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt);
// Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them).
gcInfoEncoder->FinalizeSlotIds();
// Now we can actually use those slot ID's to declare live ranges.
- gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK);
+ gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt);
if (compiler->opts.compDbgEnC)
{
}
};
-void GCInfo::gcMakeRegPtrTable(GcInfoEncoder* gcInfoEncoder,
- unsigned codeSize,
- unsigned prologSize,
- MakeRegPtrMode mode)
+void GCInfo::gcMakeRegPtrTable(
+ GcInfoEncoder* gcInfoEncoder, unsigned codeSize, unsigned prologSize, MakeRegPtrMode mode, unsigned* callCntRef)
{
GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
+ const bool noTrackedGCSlots =
+ (compiler->opts.MinOpts() && !compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT) &&
+ !JitConfig.JitMinOptsTrackGCrefs());
+
if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
{
m_regSlotMap = new (compiler->getAllocator()) RegSlotMap(compiler->getAllocator());
{
stackSlotBase = GC_FRAMEREG_REL;
}
- StackSlotIdKey sskey(varDsc->lvStkOffs, (stackSlotBase == GC_FRAMEREG_REL), flags);
- GcSlotId varSlotId;
- if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
+ if (noTrackedGCSlots)
{
- if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
+ // No need to hash/lookup untracked GC refs; just grab a new Slot Id.
+ if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
{
- varSlotId = gcInfoEncoderWithLog->GetStackSlotId(varDsc->lvStkOffs, flags, stackSlotBase);
- m_stackSlotMap->Set(sskey, varSlotId);
+ gcInfoEncoderWithLog->GetStackSlotId(varDsc->lvStkOffs, flags, stackSlotBase);
+ }
+ }
+ else
+ {
+ StackSlotIdKey sskey(varDsc->lvStkOffs, (stackSlotBase == GC_FRAMEREG_REL), flags);
+ GcSlotId varSlotId;
+ if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
+ {
+ if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
+ {
+ varSlotId = gcInfoEncoderWithLog->GetStackSlotId(varDsc->lvStkOffs, flags, stackSlotBase);
+ m_stackSlotMap->Set(sskey, varSlotId);
+ }
}
}
}
{
if (gcCallDescList != nullptr)
{
- for (CallDsc* call = gcCallDescList; call != nullptr; call = call->cdNext)
+ if (noTrackedGCSlots)
{
- numCallSites++;
+ // We have the call count from the previous run.
+ numCallSites = *callCntRef;
+
+ // If there are no calls, tell the world and bail.
+ if (numCallSites == 0)
+ {
+ gcInfoEncoderWithLog->DefineCallSites(nullptr, nullptr, 0);
+ return;
+ }
+ }
+ else
+ {
+ for (CallDsc* call = gcCallDescList; call != nullptr; call = call->cdNext)
+ {
+ numCallSites++;
+ }
}
pCallSites = new (compiler, CMK_GC) unsigned[numCallSites];
pCallSiteSizes = new (compiler, CMK_GC) BYTE[numCallSites];
// Now consider every call.
for (CallDsc* call = gcCallDescList; call != nullptr; call = call->cdNext)
{
- if (mode == MAKE_REG_PTR_MODE_DO_WORK)
- {
- pCallSites[callSiteNum] = call->cdOffs - call->cdCallInstrSize;
- pCallSiteSizes[callSiteNum] = call->cdCallInstrSize;
- callSiteNum++;
- }
-
- unsigned nextOffset;
-
// Figure out the code offset of this entry.
- nextOffset = call->cdOffs;
+ unsigned nextOffset = call->cdOffs;
// As far as I (DLD, 2010) can determine by asking around, the "call->u1.cdArgMask"
// and "cdArgCnt" cases are to handle x86 situations in which a call expression is nested as an
assert(call->cdOffs >= call->cdCallInstrSize);
// call->cdOffs is actually the offset of the instruction *following* the call, so subtract
// the call instruction size to get the offset of the actual call instruction...
- unsigned callOffset = call->cdOffs - call->cdCallInstrSize;
- // Record that these registers are live before the call...
- gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, callOffset, regMask, GC_SLOT_LIVE, byrefRegMask, nullptr);
- // ...and dead after.
- gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, call->cdOffs, regMask, GC_SLOT_DEAD, byrefRegMask,
- nullptr);
+ unsigned callOffset = nextOffset - call->cdCallInstrSize;
+
+ if (noTrackedGCSlots && regMask == 0)
+ {
+ // No live GC refs in regs at the call -> don't record the call.
+ }
+ else
+ {
+ // Append an entry for the call if doing the real thing.
+ if (mode == MAKE_REG_PTR_MODE_DO_WORK)
+ {
+ pCallSites[callSiteNum] = callOffset;
+ pCallSiteSizes[callSiteNum] = call->cdCallInstrSize;
+ }
+ callSiteNum++;
+
+ // Record that these registers are live before the call...
+ gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, callOffset, regMask, GC_SLOT_LIVE, byrefRegMask,
+ nullptr);
+ // ...and dead after.
+ gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, nextOffset, regMask, GC_SLOT_DEAD, byrefRegMask,
+ nullptr);
+ }
}
+ // Make sure we've recorded the expected number of calls
+ assert(mode != MAKE_REG_PTR_MODE_DO_WORK || numCallSites == callSiteNum);
+ // Return the actual recorded call count to the caller
+ *callCntRef = callSiteNum;
+
// OK, define the call sites.
if (mode == MAKE_REG_PTR_MODE_DO_WORK)
{
CONFIG_INTEGER(JitEnableNoWayAssert, W("JitEnableNoWayAssert"), 1)
#endif // !defined(DEBUG) && !defined(_DEBUG)
+#if !defined(JIT32_GCENCODER)
+#if defined(_TARGET_AMD64_) && defined(FEATURE_CORECLR)
+#define JitMinOptsTrackGCrefs_Default 0 // Not tracking GC refs in MinOpts is new behavior
+#else
+#define JitMinOptsTrackGCrefs_Default 1
+#endif
+CONFIG_INTEGER(JitMinOptsTrackGCrefs, W("JitMinOptsTrackGCrefs"), JitMinOptsTrackGCrefs_Default) // Track GC roots
+#endif // !defined(JIT32_GCENCODER)
+
// The following should be wrapped inside "#if MEASURE_MEM_ALLOC / #endif", but
// some files include this one without bringing in the definitions from "jit.h"
// so we don't always know what the "true" value of that flag should be. For now
// references, building up mappings from tuples of <reg/offset X byref/pinning> to the corresponding
// slot id (in the two member fields declared above). In the "do work" mode, we use these slot ids to
// actually declare live ranges to the encoder.
- void gcMakeRegPtrTable(GcInfoEncoder* gcInfoEncoder, unsigned codeSize, unsigned prologSize, MakeRegPtrMode mode);
+ void gcMakeRegPtrTable(GcInfoEncoder* gcInfoEncoder,
+ unsigned codeSize,
+ unsigned prologSize,
+ MakeRegPtrMode mode,
+ unsigned* callCntRef);
#endif
#ifdef JIT32_GCENCODER
lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_PinningRef));
#endif
}
+ else if (opts.MinOpts() && !JitConfig.JitMinOptsTrackGCrefs() && varTypeIsGC(varDsc->TypeGet()))
+ {
+ varDsc->lvTracked = 0;
+ }
// Are we not optimizing and we have exception handlers?
// if so mark all args and locals "do not enregister".
#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
+ bool noTrackedRefs = false;
+
if(m_SafePointIndex < m_NumSafePoints && !executionAborted)
{
// Skip interruptibility information
//
if(!executionAborted)
{
- _ASSERTE(m_NumInterruptibleRanges);
+ if(m_NumInterruptibleRanges == 0)
+ {
+ // No ranges and no explicit safepoint - must be MinOpts with untracked refs.
+ noTrackedRefs = true;
+ }
}
- int countIntersections = 0;
- UINT32 lastNormStop = 0;
- for(UINT32 i=0; i<m_NumInterruptibleRanges; i++)
+ if(m_NumInterruptibleRanges != 0)
{
- UINT32 normStartDelta = (UINT32) m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA1_ENCBASE );
- UINT32 normStopDelta = (UINT32) m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA2_ENCBASE ) + 1;
+ int countIntersections = 0;
+ UINT32 lastNormStop = 0;
+ for(UINT32 i=0; i<m_NumInterruptibleRanges; i++)
+ {
+ UINT32 normStartDelta = (UINT32) m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA1_ENCBASE );
+ UINT32 normStopDelta = (UINT32) m_Reader.DecodeVarLengthUnsigned( INTERRUPTIBLE_RANGE_DELTA2_ENCBASE ) + 1;
- UINT32 normStart = lastNormStop + normStartDelta;
- UINT32 normStop = normStart + normStopDelta;
- if(normBreakOffset >= normStart && normBreakOffset < normStop)
+ UINT32 normStart = lastNormStop + normStartDelta;
+ UINT32 normStop = normStart + normStopDelta;
+ if(normBreakOffset >= normStart && normBreakOffset < normStop)
+ {
+ _ASSERTE(pseudoBreakOffset == 0);
+ countIntersections++;
+ pseudoBreakOffset = numInterruptibleLength + normBreakOffset - normStart;
+ }
+ numInterruptibleLength += normStopDelta;
+ lastNormStop = normStop;
+ }
+ _ASSERTE(countIntersections <= 1);
+ if(countIntersections == 0)
{
- _ASSERTE(pseudoBreakOffset == 0);
- countIntersections++;
- pseudoBreakOffset = numInterruptibleLength + normBreakOffset - normStart;
+ _ASSERTE(executionAborted);
+ LOG((LF_GCROOTS, LL_INFO100000, "Not reporting this frame because it is aborted and not fully interruptible.\n"));
+ goto ExitSuccess;
}
- numInterruptibleLength += normStopDelta;
- lastNormStop = normStop;
- }
- _ASSERTE(countIntersections <= 1);
- if(countIntersections == 0)
- {
- _ASSERTE(executionAborted);
- LOG((LF_GCROOTS, LL_INFO100000, "Not reporting this frame because it is aborted and not fully interruptible.\n"));
- goto ExitSuccess;
}
}
#else // !PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
// Try partially interruptible first
//------------------------------------------------------------------------------
- if(executionAborted)
- {
- _ASSERTE(m_NumSafePoints == 0);
- m_Reader.Skip(m_NumSafePoints * numSlots);
- }
- else if( m_SafePointIndex != m_NumSafePoints )
+ if( !executionAborted && m_SafePointIndex != m_NumSafePoints )
{
if (numBitsPerOffset)
{
else
{
m_Reader.Skip(m_NumSafePoints * numSlots);
+ if(m_NumInterruptibleRanges == 0)
+ goto ReportUntracked;
}
#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
}
+ public static void RunTest2()
+ {
+ Dummy2 obj2 = new Dummy2();
+ obj2 = null;
+ }
+
+
public static void RunTest()
{
Dummy obj = new Dummy();
- Dummy2 obj2 = new Dummy2();
+
+ RunTest2();
// *uncomment the for loop to make test fail with complus_jitminops set
// by design as per briansul
//for (int i=0; i<5; i++) {
- obj2 = null;
GC.Collect();
GC.WaitForPendingFinalizers();
//}
MakeLeak(iObj);
}
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ GC.Collect();
+
Console.WriteLine("~LeakObject() was called {0} times.", LeakObject.icFinal);
return (LeakObject.icFinal == iObj*iRep);
}
mem[mem.Length-1] = 1;
}
- Mv_Obj = null;
-
- GC.Collect();
- GC.WaitForPendingFinalizers();
- GC.Collect();
-
}
static public ResurrectObj ResObjHolder; // Defaults to null
- // This method demonstrates how the GC supports resurrection.
+ // These methods demonstrate how the GC supports resurrection.
// NOTE: Resurrection is discouraged.
- private static void ResurrectionDemo() {
- Display(0, "\n\nDemo start: Object Resurrection.", +1);
-
+ private static void ResurrectionInit() {
// Create a ResurrectionObj
ResurrectObj obj = new ResurrectObj("Resurrection");
// Destroy all strong references to the new ResurrectionObj
obj = null;
+ }
+
+ private static void ResurrectionDemo() {
+ Display(0, "\n\nDemo start: Object Resurrection.", +1);
+
+ // Create a ResurrectionObj and drop it on the floor.
+ ResurrectionInit();
// Force the GC to determine that the object is unreachable.
Collect();
public bool RunTest(int iObj,int iSwitch)
{
DeleteObj(iObj,iSwitch);
+
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ GC.Collect();
+
bool result = CheckResult(iObj,iSwitch);
return result;
}
{
rgNode[i] = null;
}
-
- GC.Collect();
- GC.WaitForPendingFinalizers();
- GC.Collect();
-
}
public bool CheckResult(int iObj,int iSwitch)
return 100;
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
- private static void Test1()
+ private static void CollectAndFinalize()
{
- for (int i = 0; i < 50; i++)
- s_arr[i] = new Test(i);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
+ private static void Test1()
+ {
+ for (int i = 0; i < 50; i++)
+ s_arr[i] = new Test(i);
+ CollectAndFinalize();
+ }
+ [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private static void Test2()
{
for (int i = 0; i < 50; i++)
s_arr[i].CheckValid();
s_arr[i] = null;
}
- GC.Collect();
- GC.WaitForPendingFinalizers();
- GC.Collect();
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private static void Test3()
{
+ CollectAndFinalize();
for (int i = 0; i < 50; i++)
{
if (s_arr[i] == null) throw new Exception();
s_arr[i].CheckValid();
s_arr[i] = null;
}
- GC.Collect();
- GC.WaitForPendingFinalizers();
- GC.Collect();
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private static void Test4()
{
+ CollectAndFinalize();
for (int i = 0; i < 50; i++)
{
if (s_arr[i] == null) throw new Exception();
s_arr[i].CheckValid();
s_arr[i] = null;
}
- GC.Collect();
- GC.WaitForPendingFinalizers();
- GC.Collect();
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private static void Test5()
{
+ CollectAndFinalize();
for (int i = 0; i < 50; i++)
{
if (s_arr[i] == null) throw new Exception();
s_arr[i].CheckValid();
s_arr[i] = null;
}
- GC.Collect();
- GC.WaitForPendingFinalizers();
- GC.Collect();
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private static void Test6()
{
+ CollectAndFinalize();
for (int i = 0; i < 50; i++)
{
if (s_arr[i] == null) throw new Exception();