From e789d32c14a6910190daf2e7344a0e92b0ebda93 Mon Sep 17 00:00:00 2001 From: Peter Kukol Date: Thu, 23 Feb 2017 09:13:34 -0700 Subject: [PATCH] Simplified MinOpts GC ref tracking. (dotnet/coreclr#9231) Simplified MinOpts GC ref tracking - when a method is compiled with MinOpts for targets with the 64-bit GC info encoder we mark all GC slots as untracked and we omit encoding call sites with no live tracked GC refs explicitly in the GC tables; this can be controlled using the new COMPlus_JitMinOptsTrackGCrefs environment flag. Commit migrated from https://github.com/dotnet/coreclr/commit/d7509df9635ca92c1a3cf75dc2516ae51b0e03d6 --- src/coreclr/src/jit/codegenarm.cpp | 6 +- src/coreclr/src/jit/codegenarm64.cpp | 7 +- src/coreclr/src/jit/codegenlegacy.cpp | 6 +- src/coreclr/src/jit/codegenxarch.cpp | 6 +- src/coreclr/src/jit/gcencode.cpp | 98 +++++++++++++++------- src/coreclr/src/jit/jitconfigvalues.h | 9 ++ src/coreclr/src/jit/jitgcinfo.h | 6 +- src/coreclr/src/jit/lclvars.cpp | 4 + src/coreclr/src/vm/gcinfodecoder.cpp | 55 +++++++----- src/coreclr/tests/src/GC/API/GC/KeepAlive.cs | 11 ++- .../tests/src/GC/Scenarios/LeakGen/leakgen.cs | 10 +-- src/coreclr/tests/src/GC/Scenarios/Samples/gc.cs | 13 ++- .../src/GC/Scenarios/WeakReference/weakreffinal.cs | 10 +-- .../tests/src/JIT/Methodical/Arrays/misc/arrres.cs | 27 +++--- 14 files changed, 177 insertions(+), 91 deletions(-) diff --git a/src/coreclr/src/jit/codegenarm.cpp b/src/coreclr/src/jit/codegenarm.cpp index 580cad5..88f576e 100644 --- a/src/coreclr/src/jit/codegenarm.cpp +++ b/src/coreclr/src/jit/codegenarm.cpp @@ -1916,12 +1916,14 @@ void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize, // 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(); diff --git a/src/coreclr/src/jit/codegenarm64.cpp b/src/coreclr/src/jit/codegenarm64.cpp index 0fb3fb8..2fac6af 100644 --- a/src/coreclr/src/jit/codegenarm64.cpp +++ b/src/coreclr/src/jit/codegenarm64.cpp @@ -6055,14 +6055,17 @@ void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize // 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) { diff --git a/src/coreclr/src/jit/codegenlegacy.cpp b/src/coreclr/src/jit/codegenlegacy.cpp index c616546..d61dda5 100644 --- a/src/coreclr/src/jit/codegenlegacy.cpp +++ b/src/coreclr/src/jit/codegenlegacy.cpp @@ -20152,12 +20152,14 @@ void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize // 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(); diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index e893da6..8499d3a 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -8354,12 +8354,14 @@ void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize // 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) { diff --git a/src/coreclr/src/jit/gcencode.cpp b/src/coreclr/src/jit/gcencode.cpp index dcca19e..2b9fe37 100644 --- a/src/coreclr/src/jit/gcencode.cpp +++ b/src/coreclr/src/jit/gcencode.cpp @@ -3844,13 +3844,15 @@ struct InterruptibleRangeReporter } }; -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()); @@ -3961,14 +3963,25 @@ void GCInfo::gcMakeRegPtrTable(GcInfoEncoder* gcInfoEncoder, { 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); + } } } } @@ -4204,9 +4217,24 @@ void GCInfo::gcMakeRegPtrTable(GcInfoEncoder* gcInfoEncoder, { 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]; @@ -4216,17 +4244,8 @@ void GCInfo::gcMakeRegPtrTable(GcInfoEncoder* gcInfoEncoder, // 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 @@ -4251,13 +4270,36 @@ void GCInfo::gcMakeRegPtrTable(GcInfoEncoder* gcInfoEncoder, 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) { diff --git a/src/coreclr/src/jit/jitconfigvalues.h b/src/coreclr/src/jit/jitconfigvalues.h index a3e30de..264b661 100644 --- a/src/coreclr/src/jit/jitconfigvalues.h +++ b/src/coreclr/src/jit/jitconfigvalues.h @@ -214,6 +214,15 @@ CONFIG_INTEGER(JitEnableNoWayAssert, W("JitEnableNoWayAssert"), 0) 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 diff --git a/src/coreclr/src/jit/jitgcinfo.h b/src/coreclr/src/jit/jitgcinfo.h index 3f8d8af..7b17b84 100644 --- a/src/coreclr/src/jit/jitgcinfo.h +++ b/src/coreclr/src/jit/jitgcinfo.h @@ -295,7 +295,11 @@ public: // references, building up mappings from tuples of 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 diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index fa1ba82..f0d6903 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -2965,6 +2965,10 @@ void Compiler::lvaSortByRefCount() 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". diff --git a/src/coreclr/src/vm/gcinfodecoder.cpp b/src/coreclr/src/vm/gcinfodecoder.cpp index 89f4704..5dd52b9 100644 --- a/src/coreclr/src/vm/gcinfodecoder.cpp +++ b/src/coreclr/src/vm/gcinfodecoder.cpp @@ -628,6 +628,8 @@ bool GcInfoDecoder::EnumerateLiveSlots( #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED + bool noTrackedRefs = false; + if(m_SafePointIndex < m_NumSafePoints && !executionAborted) { // Skip interruptibility information @@ -648,33 +650,40 @@ bool GcInfoDecoder::EnumerateLiveSlots( // 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= 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 @@ -718,7 +727,7 @@ bool GcInfoDecoder::EnumerateLiveSlots( if(executionAborted) { - _ASSERTE(m_NumSafePoints == 0); + // Skip over safepoint info (if any is present). m_Reader.Skip(m_NumSafePoints * numSlots); } else if( m_SafePointIndex != m_NumSafePoints ) @@ -787,6 +796,8 @@ bool GcInfoDecoder::EnumerateLiveSlots( else { m_Reader.Skip(m_NumSafePoints * numSlots); + if(noTrackedRefs) + goto ReportUntracked; } #endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED diff --git a/src/coreclr/tests/src/GC/API/GC/KeepAlive.cs b/src/coreclr/tests/src/GC/API/GC/KeepAlive.cs index df3d9d2..3174f89 100644 --- a/src/coreclr/tests/src/GC/API/GC/KeepAlive.cs +++ b/src/coreclr/tests/src/GC/API/GC/KeepAlive.cs @@ -47,16 +47,23 @@ public class Test } + 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(); //} diff --git a/src/coreclr/tests/src/GC/Scenarios/LeakGen/leakgen.cs b/src/coreclr/tests/src/GC/Scenarios/LeakGen/leakgen.cs index 9235256..8ec6531 100644 --- a/src/coreclr/tests/src/GC/Scenarios/LeakGen/leakgen.cs +++ b/src/coreclr/tests/src/GC/Scenarios/LeakGen/leakgen.cs @@ -62,6 +62,10 @@ namespace LGen { MakeLeak(iObj); } + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + Console.WriteLine("~LeakObject() was called {0} times.", LeakObject.icFinal); return (LeakObject.icFinal == iObj*iRep); } @@ -80,12 +84,6 @@ namespace LGen { mem[mem.Length-1] = 1; } - Mv_Obj = null; - - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - } diff --git a/src/coreclr/tests/src/GC/Scenarios/Samples/gc.cs b/src/coreclr/tests/src/GC/Scenarios/Samples/gc.cs index a6c1cfe..7ed11bc 100644 --- a/src/coreclr/tests/src/GC/Scenarios/Samples/gc.cs +++ b/src/coreclr/tests/src/GC/Scenarios/Samples/gc.cs @@ -273,16 +273,21 @@ class Application { 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(); diff --git a/src/coreclr/tests/src/GC/Scenarios/WeakReference/weakreffinal.cs b/src/coreclr/tests/src/GC/Scenarios/WeakReference/weakreffinal.cs index a356cec..754e88d 100644 --- a/src/coreclr/tests/src/GC/Scenarios/WeakReference/weakreffinal.cs +++ b/src/coreclr/tests/src/GC/Scenarios/WeakReference/weakreffinal.cs @@ -22,6 +22,11 @@ namespace DefaultNamespace { public bool RunTest(int iObj,int iSwitch) { DeleteObj(iObj,iSwitch); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + bool result = CheckResult(iObj,iSwitch); return result; } @@ -48,11 +53,6 @@ namespace DefaultNamespace { { rgNode[i] = null; } - - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - } public bool CheckResult(int iObj,int iSwitch) diff --git a/src/coreclr/tests/src/JIT/Methodical/Arrays/misc/arrres.cs b/src/coreclr/tests/src/JIT/Methodical/Arrays/misc/arrres.cs index 7956cc6..e021a59 100644 --- a/src/coreclr/tests/src/JIT/Methodical/Arrays/misc/arrres.cs +++ b/src/coreclr/tests/src/JIT/Methodical/Arrays/misc/arrres.cs @@ -45,15 +45,20 @@ namespace GCTest 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++) @@ -62,52 +67,44 @@ namespace GCTest 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(); -- 2.7.4