From ad42b512eac2e3ccc70e17ad82f1a77635268ace Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 2 Nov 2023 18:33:32 -0700 Subject: [PATCH 1/5] JIT: cross-block local assertion prop in morph During global morph, allow assertions to propagate to a block from the block's predecessors. Handle special cases where we can't allow this to happen: * block has preds that have not yet been morphed * block has no preds * block is specially flagged as one that might gain new preds during morph * block is an EH handler entry Contributes to #93246. --- src/coreclr/jit/assertionprop.cpp | 5 +- src/coreclr/jit/jitconfigvalues.h | 3 ++ src/coreclr/jit/morph.cpp | 79 +++++++++++++++++++++++++++++-- 3 files changed, 81 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index a633f7f962f117..ba0806fa8ccb22 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -4379,12 +4379,11 @@ AssertionIndex Compiler::optAssertionIsNonNullInternal(GenTree* op, // Find live assertions related to lclNum // unsigned const lclNum = op->AsLclVarCommon()->GetLclNum(); - ASSERT_TP apDependent = GetAssertionDep(lclNum); - BitVecOps::IntersectionD(apTraits, apDependent, apLocal); + ASSERT_TP apDependent = BitVecOps::Intersection(apTraits, GetAssertionDep(lclNum), assertions); // Scan those looking for a suitable assertion // - BitVecOps::Iter iter(apTraits, assertions); + BitVecOps::Iter iter(apTraits, apDependent); unsigned index = 0; while (iter.NextElem(&index)) { diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index f6460feddec126..33d97635a73fc3 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -445,6 +445,9 @@ CONFIG_INTEGER(JitNoRngChks, W("JitNoRngChks"), 0) // If 1, don't generate range #if defined(OPT_CONFIG) CONFIG_INTEGER(JitDoAssertionProp, W("JitDoAssertionProp"), 1) // Perform assertion propagation optimization +CONFIG_INTEGER(JitDoCrossBlockLocalAssertionProp, W("JitDoCrossBlockLocalAssertionProp"), 1) // Perform local assertion + // propagation optimization + // cross-block CONFIG_INTEGER(JitDoCopyProp, W("JitDoCopyProp"), 1) // Perform copy propagation on variables that appear redundant CONFIG_INTEGER(JitDoEarlyProp, W("JitDoEarlyProp"), 1) // Perform Early Value Propagation CONFIG_INTEGER(JitDoLoopHoisting, W("JitDoLoopHoisting"), 1) // Perform loop hoisting on loop invariant values diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index ec70cb0025efac..627735493e642d 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -13780,10 +13780,78 @@ void Compiler::fgMorphBlock(BasicBlock* block) if (optLocalAssertionProp) { - // For now, each block starts with an empty table, and no available assertions + // Determine if this block can leverage assertions from its pred blocks. // - optAssertionReset(0); - apLocal = BitVecOps::MakeEmpty(apTraits); + // Some blocks are ineligible. + // + bool canUsePredAssertions = ((block->bbFlags & BBF_CAN_ADD_PRED) == 0) && !bbIsHandlerBeg(block); + +#ifdef DEBUG + // Optionally suppress via config + // + if (JitConfig.JitDoCrossBlockLocalAssertionProp() == 0) + { + canUsePredAssertions = false; + } +#endif + + // Validate all preds have valid info + // + if (canUsePredAssertions) + { + bool hasPred = false; + + for (BasicBlock* const pred : block->PredBlocks()) + { + // A higher postorder number means the block appears earlier in + // the postorder. Use this to detect if a pred's assertion info is available. + // + if (pred->bbPostorderNum > block->bbPostorderNum) + { + // Yes, available. If this is the first pred, copy. + // If this is a subsequent pred, intersect. + // + if (!hasPred) + { + apLocal = BitVecOps::MakeCopy(apTraits, pred->bbAssertionOut); + hasPred = true; + } + else + { + BitVecOps::IntersectionD(apTraits, apLocal, pred->bbAssertionOut); + } + + continue; + } + + // No, not available. + // + JITDUMP(FMT_BB " pred " FMT_BB " not processed; clearing assertions in\n", block->bbNum, pred->bbNum); + canUsePredAssertions = false; + } + + // If there were no preds, there are no asserions in. + // + if (!hasPred) + { + JITDUMP(FMT_BB " has no preds, so no assertions in\n", block->bbNum); + canUsePredAssertions = false; + } + + if (canUsePredAssertions) + { + JITDUMPEXEC(optDumpAssertionIndices("Assertions in: ", apLocal)); + } + } + else + { + JITDUMP(FMT_BB " ineligible for cross-block; clearing assertions in\n", block->bbNum); + } + + if (!canUsePredAssertions) + { + apLocal = BitVecOps::MakeEmpty(apTraits); + } } // Make the current basic block address available globally. @@ -13801,6 +13869,11 @@ void Compiler::fgMorphBlock(BasicBlock* block) } } + if (optLocalAssertionProp && (block->NumSucc() > 0)) + { + block->bbAssertionOut = BitVecOps::MakeCopy(apTraits, apLocal); + } + compCurBB = nullptr; } From cceb9f8843ecae9cb40c021c36f8c2d8e96bb7eb Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 6 Nov 2023 19:01:56 -0800 Subject: [PATCH 2/5] no cross block for methods with lots of locals -- the table fills up too fast --- src/coreclr/jit/assertionprop.cpp | 27 +++++++ src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/morph.cpp | 114 ++++++++++++++++-------------- 3 files changed, 88 insertions(+), 54 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index ba0806fa8ccb22..b9078e19e44dc3 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -541,6 +541,33 @@ void Compiler::optAssertionTraitsInit(AssertionIndex assertionCount) void Compiler::optAssertionInit(bool isLocalProp) { + // See if we should disable cross-block local prop + // + if (optCrossBlockLocalAssertionProp) + { +#ifdef DEBUG + // Disable via config + // + if (JitConfig.JitDoCrossBlockLocalAssertionProp() == 0) + { + JITDUMP("Disabling cross-block assertion prop by config setting\n"); + optCrossBlockLocalAssertionProp = false; + } +#endif + + // Disable if too many locals + // + // The typical number of local assertions is roughly proportional + // to the number of locals. So when we have huge numbers of locals, + // just do within-block local assertion prop. + // + if (lvaCount > (unsigned)JitConfig.JitMaxLocalsToTrack()) + { + JITDUMP("Disabling cross-block assertion prop: too many locals\n"); + optCrossBlockLocalAssertionProp = false; + } + } + // Use a function countFunc to determine a proper maximum assertion count for the // method being compiled. The function is linear to the IL size for small and // moderate methods. For large methods, considering throughput impact, we track no diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 77d2e0d35f3837..80740ebb53bb95 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7640,6 +7640,7 @@ class Compiler AssertionDsc* optAssertionTabPrivate; // table that holds info about value assignments AssertionIndex optAssertionCount; // total number of assertions in the assertion table AssertionIndex optMaxAssertionCount; + bool optCrossBlockLocalAssertionProp; unsigned optAssertionOverflow; bool optCanPropLclVar; bool optCanPropEqual; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 627735493e642d..6def1bfcc2069e 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -13780,78 +13780,80 @@ void Compiler::fgMorphBlock(BasicBlock* block) if (optLocalAssertionProp) { - // Determine if this block can leverage assertions from its pred blocks. - // - // Some blocks are ineligible. - // - bool canUsePredAssertions = ((block->bbFlags & BBF_CAN_ADD_PRED) == 0) && !bbIsHandlerBeg(block); - -#ifdef DEBUG - // Optionally suppress via config - // - if (JitConfig.JitDoCrossBlockLocalAssertionProp() == 0) + if (!optCrossBlockLocalAssertionProp) { - canUsePredAssertions = false; + // For now, each block starts with an empty table, and no available assertions + // + optAssertionReset(0); + apLocal = BitVecOps::MakeEmpty(apTraits); } -#endif - - // Validate all preds have valid info - // - if (canUsePredAssertions) + else { - bool hasPred = false; + // Determine if this block can leverage assertions from its pred blocks. + // + // Some blocks are ineligible. + // + bool canUsePredAssertions = ((block->bbFlags & BBF_CAN_ADD_PRED) == 0) && !bbIsHandlerBeg(block); - for (BasicBlock* const pred : block->PredBlocks()) + // Validate all preds have valid info + // + if (canUsePredAssertions) { - // A higher postorder number means the block appears earlier in - // the postorder. Use this to detect if a pred's assertion info is available. - // - if (pred->bbPostorderNum > block->bbPostorderNum) + bool hasPred = false; + + for (BasicBlock* const pred : block->PredBlocks()) { - // Yes, available. If this is the first pred, copy. - // If this is a subsequent pred, intersect. + // A higher postorder number means the block appears earlier in + // the postorder. Use this to detect if a pred's assertion info is available. // - if (!hasPred) - { - apLocal = BitVecOps::MakeCopy(apTraits, pred->bbAssertionOut); - hasPred = true; - } - else + if (pred->bbPostorderNum > block->bbPostorderNum) { - BitVecOps::IntersectionD(apTraits, apLocal, pred->bbAssertionOut); + // Yes, available. If this is the first pred, copy. + // If this is a subsequent pred, intersect. + // + if (!hasPred) + { + apLocal = BitVecOps::MakeCopy(apTraits, pred->bbAssertionOut); + hasPred = true; + } + else + { + BitVecOps::IntersectionD(apTraits, apLocal, pred->bbAssertionOut); + } + + continue; } - continue; + // No, not available. + // + JITDUMP(FMT_BB " pred " FMT_BB " not processed; clearing assertions in\n", block->bbNum, + pred->bbNum); + canUsePredAssertions = false; } - // No, not available. + // If there were no preds, there are no asserions in. // - JITDUMP(FMT_BB " pred " FMT_BB " not processed; clearing assertions in\n", block->bbNum, pred->bbNum); - canUsePredAssertions = false; - } + if (!hasPred) + { + JITDUMP(FMT_BB " has no preds, so no assertions in\n", block->bbNum); + canUsePredAssertions = false; + } - // If there were no preds, there are no asserions in. - // - if (!hasPred) + if (canUsePredAssertions) + { + JITDUMPEXEC(optDumpAssertionIndices("Assertions in: ", apLocal)); + } + } + else { - JITDUMP(FMT_BB " has no preds, so no assertions in\n", block->bbNum); - canUsePredAssertions = false; + JITDUMP(FMT_BB " ineligible for cross-block; clearing assertions in\n", block->bbNum); } - if (canUsePredAssertions) + if (!canUsePredAssertions) { - JITDUMPEXEC(optDumpAssertionIndices("Assertions in: ", apLocal)); + apLocal = BitVecOps::MakeEmpty(apTraits); } } - else - { - JITDUMP(FMT_BB " ineligible for cross-block; clearing assertions in\n", block->bbNum); - } - - if (!canUsePredAssertions) - { - apLocal = BitVecOps::MakeEmpty(apTraits); - } } // Make the current basic block address available globally. @@ -13869,8 +13871,11 @@ void Compiler::fgMorphBlock(BasicBlock* block) } } - if (optLocalAssertionProp && (block->NumSucc() > 0)) + // Publish the live out state. + // + if (optCrossBlockLocalAssertionProp && (block->NumSucc() > 0)) { + assert(optLocalAssertionProp); block->bbAssertionOut = BitVecOps::MakeCopy(apTraits, apLocal); } @@ -13895,7 +13900,8 @@ PhaseStatus Compiler::fgMorphBlocks() // Local assertion prop is enabled if we are optimized // - optLocalAssertionProp = opts.OptimizationEnabled(); + optLocalAssertionProp = opts.OptimizationEnabled(); + optCrossBlockLocalAssertionProp = optLocalAssertionProp; if (optLocalAssertionProp) { From bc59375de461b44632c7afa636b8c8a25a756633 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Tue, 7 Nov 2023 09:10:01 -0800 Subject: [PATCH 3/5] assertion limit scaling --- src/coreclr/jit/assertionprop.cpp | 100 ++++++++++++++++++------------ 1 file changed, 62 insertions(+), 38 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index b9078e19e44dc3..a890695156d9b4 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -541,63 +541,87 @@ void Compiler::optAssertionTraitsInit(AssertionIndex assertionCount) void Compiler::optAssertionInit(bool isLocalProp) { - // See if we should disable cross-block local prop + assert(NO_ASSERTION_INDEX == 0); + const unsigned maxTrackedLocals = (unsigned)JitConfig.JitMaxLocalsToTrack(); + + // We initialize differently for local prop / global prop // - if (optCrossBlockLocalAssertionProp) + if (isLocalProp) { -#ifdef DEBUG - // Disable via config + optLocalAssertionProp = true; + + // See if we should disable cross-block local prop // - if (JitConfig.JitDoCrossBlockLocalAssertionProp() == 0) + if (optCrossBlockLocalAssertionProp) { - JITDUMP("Disabling cross-block assertion prop by config setting\n"); - optCrossBlockLocalAssertionProp = false; - } +#ifdef DEBUG + // Disable via config + // + if (JitConfig.JitDoCrossBlockLocalAssertionProp() == 0) + { + JITDUMP("Disabling cross-block assertion prop by config setting\n"); + optCrossBlockLocalAssertionProp = false; + } #endif - // Disable if too many locals - // - // The typical number of local assertions is roughly proportional - // to the number of locals. So when we have huge numbers of locals, - // just do within-block local assertion prop. - // - if (lvaCount > (unsigned)JitConfig.JitMaxLocalsToTrack()) + // Disable if too many locals + // + // The typical number of local assertions is roughly proportional + // to the number of locals. So when we have huge numbers of locals, + // just do within-block local assertion prop. + // + if (lvaCount > maxTrackedLocals) + { + JITDUMP("Disabling cross-block assertion prop: too many locals\n"); + optCrossBlockLocalAssertionProp = false; + } + } + + if (optCrossBlockLocalAssertionProp) { - JITDUMP("Disabling cross-block assertion prop: too many locals\n"); - optCrossBlockLocalAssertionProp = false; + // We know lvaCount is less than the tracked limit. + // Allow for roughly one assertion per local, up to the tracked limit. + // (empirical studies show about 0.6 asserions/local) + // + optMaxAssertionCount = (AssertionIndex)min(maxTrackedLocals, ((lvaCount / 64) + 1) * 64); + } + else + { + // The table will be reset for each block, so can be smaller. + // + optMaxAssertionCount = 64; } - } - // Use a function countFunc to determine a proper maximum assertion count for the - // method being compiled. The function is linear to the IL size for small and - // moderate methods. For large methods, considering throughput impact, we track no - // more than 64 assertions. - // Note this tracks at most only 256 assertions. - static const AssertionIndex countFunc[] = {64, 128, 256, 64}; - static const unsigned lowerBound = 0; - static const unsigned upperBound = ArrLen(countFunc) - 1; - const unsigned codeSize = info.compILCodeSize / 512; - optMaxAssertionCount = countFunc[isLocalProp ? lowerBound : min(upperBound, codeSize)]; + optAssertionDep = + new (this, CMK_AssertionProp) JitExpandArray(getAllocator(CMK_AssertionProp), max(1, lvaCount)); + } + else + { + // General assertion prop. + // + optLocalAssertionProp = false; - optLocalAssertionProp = isLocalProp; - optAssertionTabPrivate = new (this, CMK_AssertionProp) AssertionDsc[optMaxAssertionCount]; - assert(NO_ASSERTION_INDEX == 0); + // Use a function countFunc to determine a proper maximum assertion count for the + // method being compiled. The function is linear to the IL size for small and + // moderate methods. For large methods, considering throughput impact, we track no + // more than 64 assertions. + // Note this tracks at most only 256 assertions. + // + static const AssertionIndex countFunc[] = {64, 128, 256, 64}; + static const unsigned upperBound = ArrLen(countFunc) - 1; + const unsigned codeSize = info.compILCodeSize / 512; + optMaxAssertionCount = countFunc[min(upperBound, codeSize)]; - if (!isLocalProp) - { optValueNumToAsserts = new (getAllocator(CMK_AssertionProp)) ValueNumToAssertsMap(getAllocator(CMK_AssertionProp)); optComplementaryAssertionMap = new (this, CMK_AssertionProp) AssertionIndex[optMaxAssertionCount + 1](); // zero-inited (NO_ASSERTION_INDEX) } - if (optAssertionDep == nullptr) - { - optAssertionDep = - new (this, CMK_AssertionProp) JitExpandArray(getAllocator(CMK_AssertionProp), max(1, lvaCount)); - } + optAssertionTabPrivate = new (this, CMK_AssertionProp) AssertionDsc[optMaxAssertionCount]; optAssertionTraitsInit(optMaxAssertionCount); + optAssertionCount = 0; optAssertionOverflow = 0; optAssertionPropagated = false; From 2b78bdc07357c8dbd091bf96e2ce90ce6cfa36f5 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 8 Nov 2023 15:25:07 -0800 Subject: [PATCH 4/5] disable by default (for now), enable in jit-experimental --- eng/pipelines/common/templates/runtimes/run-test-job.yml | 2 +- src/coreclr/jit/assertionprop.cpp | 2 -- src/coreclr/jit/jitconfigvalues.h | 6 +++--- src/coreclr/jit/morph.cpp | 2 +- src/tests/Common/testenvironment.proj | 2 ++ 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/eng/pipelines/common/templates/runtimes/run-test-job.yml b/eng/pipelines/common/templates/runtimes/run-test-job.yml index df3228f2764a86..a2e13dca489deb 100644 --- a/eng/pipelines/common/templates/runtimes/run-test-job.yml +++ b/eng/pipelines/common/templates/runtimes/run-test-job.yml @@ -585,7 +585,7 @@ jobs: - jitobjectstackallocation - jitphysicalpromotion_only - jitphysicalpromotion_full - + - jitcrossblocklocalassertionprop ${{ if in(parameters.testGroup, 'jit-cfg') }}: scenarios: - jitcfg diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index a890695156d9b4..84e09b1884bac7 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -554,7 +554,6 @@ void Compiler::optAssertionInit(bool isLocalProp) // if (optCrossBlockLocalAssertionProp) { -#ifdef DEBUG // Disable via config // if (JitConfig.JitDoCrossBlockLocalAssertionProp() == 0) @@ -562,7 +561,6 @@ void Compiler::optAssertionInit(bool isLocalProp) JITDUMP("Disabling cross-block assertion prop by config setting\n"); optCrossBlockLocalAssertionProp = false; } -#endif // Disable if too many locals // diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 33d97635a73fc3..96c8a846272c7b 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -445,9 +445,6 @@ CONFIG_INTEGER(JitNoRngChks, W("JitNoRngChks"), 0) // If 1, don't generate range #if defined(OPT_CONFIG) CONFIG_INTEGER(JitDoAssertionProp, W("JitDoAssertionProp"), 1) // Perform assertion propagation optimization -CONFIG_INTEGER(JitDoCrossBlockLocalAssertionProp, W("JitDoCrossBlockLocalAssertionProp"), 1) // Perform local assertion - // propagation optimization - // cross-block CONFIG_INTEGER(JitDoCopyProp, W("JitDoCopyProp"), 1) // Perform copy propagation on variables that appear redundant CONFIG_INTEGER(JitDoEarlyProp, W("JitDoEarlyProp"), 1) // Perform Early Value Propagation CONFIG_INTEGER(JitDoLoopHoisting, W("JitDoLoopHoisting"), 1) // Perform loop hoisting on loop invariant values @@ -656,6 +653,9 @@ CONFIG_INTEGER(JitEnableHeadTailMerge, W("JitEnableHeadTailMerge"), 1) // Enable physical promotion CONFIG_INTEGER(JitEnablePhysicalPromotion, W("JitEnablePhysicalPromotion"), 1) +// Enable cross-block local assertion prop +CONFIG_INTEGER(JitDoCrossBlockLocalAssertionProp, W("JitDoCrossBlockLocalAssertionProp"), 0) + #if defined(DEBUG) // JitFunctionFile: Name of a file that contains a list of functions. If the currently compiled function is in the // file, certain other JIT config variables will be active. If the currently compiled function is not in the file, diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 6def1bfcc2069e..7a5a31edada352 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -13782,7 +13782,7 @@ void Compiler::fgMorphBlock(BasicBlock* block) { if (!optCrossBlockLocalAssertionProp) { - // For now, each block starts with an empty table, and no available assertions + // Each block starts with an empty table, and no available assertions // optAssertionReset(0); apLocal = BitVecOps::MakeEmpty(apTraits); diff --git a/src/tests/Common/testenvironment.proj b/src/tests/Common/testenvironment.proj index bf70dac353e882..0a06c2918dc94e 100644 --- a/src/tests/Common/testenvironment.proj +++ b/src/tests/Common/testenvironment.proj @@ -80,6 +80,7 @@ RunningIlasmRoundTrip; DOTNET_JitSynthesizeCounts; DOTNET_JitCheckSynthesizedCounts + DOTNET_JitDoCrossBlockLocalAssertionProp @@ -220,6 +221,7 @@ + From 8fa23188598a1c0d330a5f88fa2f6d2ff1e0fd1f Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 8 Nov 2023 18:55:45 -0800 Subject: [PATCH 5/5] review feedback --- src/coreclr/jit/assertionprop.cpp | 44 +++++++------- src/coreclr/jit/jitconfigvalues.h | 2 +- src/coreclr/jit/morph.cpp | 87 ++++++++++++--------------- src/tests/Common/testenvironment.proj | 2 +- 4 files changed, 62 insertions(+), 73 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 84e09b1884bac7..0aa32b4767a80d 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -548,36 +548,32 @@ void Compiler::optAssertionInit(bool isLocalProp) // if (isLocalProp) { - optLocalAssertionProp = true; + optLocalAssertionProp = true; + optCrossBlockLocalAssertionProp = true; - // See if we should disable cross-block local prop + // Disable via config // - if (optCrossBlockLocalAssertionProp) + if (JitConfig.JitEnableCrossBlockLocalAssertionProp() == 0) { - // Disable via config - // - if (JitConfig.JitDoCrossBlockLocalAssertionProp() == 0) - { - JITDUMP("Disabling cross-block assertion prop by config setting\n"); - optCrossBlockLocalAssertionProp = false; - } + JITDUMP("Disabling cross-block assertion prop by config setting\n"); + optCrossBlockLocalAssertionProp = false; + } - // Disable if too many locals - // - // The typical number of local assertions is roughly proportional - // to the number of locals. So when we have huge numbers of locals, - // just do within-block local assertion prop. - // - if (lvaCount > maxTrackedLocals) - { - JITDUMP("Disabling cross-block assertion prop: too many locals\n"); - optCrossBlockLocalAssertionProp = false; - } + // Disable if too many locals + // + // The typical number of local assertions is roughly proportional + // to the number of locals. So when we have huge numbers of locals, + // just do within-block local assertion prop. + // + if (lvaCount > maxTrackedLocals) + { + JITDUMP("Disabling cross-block assertion prop: too many locals\n"); + optCrossBlockLocalAssertionProp = false; } if (optCrossBlockLocalAssertionProp) { - // We know lvaCount is less than the tracked limit. + // We may need a fairly large table. // Allow for roughly one assertion per local, up to the tracked limit. // (empirical studies show about 0.6 asserions/local) // @@ -585,11 +581,13 @@ void Compiler::optAssertionInit(bool isLocalProp) } else { - // The table will be reset for each block, so can be smaller. + // The assertion table will be reset for each block, so it can be smaller. // optMaxAssertionCount = 64; } + // Local assertion prop keeps mappings from each local var to the assertions about that var. + // optAssertionDep = new (this, CMK_AssertionProp) JitExpandArray(getAllocator(CMK_AssertionProp), max(1, lvaCount)); } diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 96c8a846272c7b..65afd348819e11 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -654,7 +654,7 @@ CONFIG_INTEGER(JitEnableHeadTailMerge, W("JitEnableHeadTailMerge"), 1) CONFIG_INTEGER(JitEnablePhysicalPromotion, W("JitEnablePhysicalPromotion"), 1) // Enable cross-block local assertion prop -CONFIG_INTEGER(JitDoCrossBlockLocalAssertionProp, W("JitDoCrossBlockLocalAssertionProp"), 0) +CONFIG_INTEGER(JitEnableCrossBlockLocalAssertionProp, W("JitEnableCrossBlockLocalAssertionProp"), 0) #if defined(DEBUG) // JitFunctionFile: Name of a file that contains a list of functions. If the currently compiled function is in the diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 7a5a31edada352..275be7c9b17c23 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -13797,62 +13797,55 @@ void Compiler::fgMorphBlock(BasicBlock* block) // Validate all preds have valid info // - if (canUsePredAssertions) + if (!canUsePredAssertions) { - bool hasPred = false; + JITDUMP(FMT_BB " ineligible for cross-block\n", block->bbNum); + } + else + { + bool hasPredAssertions = false; for (BasicBlock* const pred : block->PredBlocks()) { - // A higher postorder number means the block appears earlier in - // the postorder. Use this to detect if a pred's assertion info is available. + // A smaller pred postorder number means the pred appears later in the postorder. + // An equal number means pred == block (block is a self-loop). + // Either way the assertion info is not available, and we must assume the worst. // - if (pred->bbPostorderNum > block->bbPostorderNum) + if (pred->bbPostorderNum <= block->bbPostorderNum) { - // Yes, available. If this is the first pred, copy. - // If this is a subsequent pred, intersect. - // - if (!hasPred) - { - apLocal = BitVecOps::MakeCopy(apTraits, pred->bbAssertionOut); - hasPred = true; - } - else - { - BitVecOps::IntersectionD(apTraits, apLocal, pred->bbAssertionOut); - } - - continue; + JITDUMP(FMT_BB " pred " FMT_BB " not processed; clearing assertions in\n", block->bbNum, + pred->bbNum); + break; } - // No, not available. + // Yes, pred assertions are available. If this is the first pred, copy. + // If this is a subsequent pred, intersect. // - JITDUMP(FMT_BB " pred " FMT_BB " not processed; clearing assertions in\n", block->bbNum, - pred->bbNum); - canUsePredAssertions = false; + if (!hasPredAssertions) + { + apLocal = BitVecOps::MakeCopy(apTraits, pred->bbAssertionOut); + hasPredAssertions = true; + } + else + { + BitVecOps::IntersectionD(apTraits, apLocal, pred->bbAssertionOut); + } } - // If there were no preds, there are no asserions in. - // - if (!hasPred) + if (!hasPredAssertions) { - JITDUMP(FMT_BB " has no preds, so no assertions in\n", block->bbNum); + // Either no preds, or some preds w/o assertions. + // canUsePredAssertions = false; } - - if (canUsePredAssertions) - { - JITDUMPEXEC(optDumpAssertionIndices("Assertions in: ", apLocal)); - } - } - else - { - JITDUMP(FMT_BB " ineligible for cross-block; clearing assertions in\n", block->bbNum); } if (!canUsePredAssertions) { apLocal = BitVecOps::MakeEmpty(apTraits); } + + JITDUMPEXEC(optDumpAssertionIndices("Assertions in: ", apLocal)); } } @@ -13898,16 +13891,18 @@ PhaseStatus Compiler::fgMorphBlocks() // fgGlobalMorph = true; - // Local assertion prop is enabled if we are optimized - // - optLocalAssertionProp = opts.OptimizationEnabled(); - optCrossBlockLocalAssertionProp = optLocalAssertionProp; - - if (optLocalAssertionProp) + if (opts.OptimizationEnabled()) { - // Initialize for local assertion prop + // Local assertion prop is enabled if we are optimizing. // - optAssertionInit(true); + optAssertionInit(/* isLocalProp*/ true); + } + else + { + // Not optimizing. No assertion prop. + // + optLocalAssertionProp = false; + optCrossBlockLocalAssertionProp = false; } if (!compEnregLocals()) @@ -13934,10 +13929,6 @@ PhaseStatus Compiler::fgMorphBlocks() { // If we aren't optimizing, we just morph in normal bbNext order. // - // Note morph can add blocks downstream from the current block, - // and alter (but not null out) the current block's bbNext; - // this iterator ensures they all get visited. - // for (BasicBlock* block : Blocks()) { fgMorphBlock(block); diff --git a/src/tests/Common/testenvironment.proj b/src/tests/Common/testenvironment.proj index 0a06c2918dc94e..1613582faf6ee7 100644 --- a/src/tests/Common/testenvironment.proj +++ b/src/tests/Common/testenvironment.proj @@ -221,7 +221,7 @@ - +