@@ -7104,6 +7104,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA
71047104 if (optLocalAssertionProp)
71057105 {
71067106 isQmarkColon = true;
7107+ BitVecOps::ClearD(apTraits, apLocalPostorder);
71077108 }
71087109 break;
71097110
@@ -7744,6 +7745,40 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA
77447745 qmarkOp2 = oldTree->AsOp()->gtOp2->AsOp()->gtOp2;
77457746 }
77467747
7748+ // During global morph, give assertion prop another shot at this tree.
7749+ //
7750+ // We need to use the "postorder" assertion set here, because apLocal
7751+ // may reflect results from subtrees that have since been reordered.
7752+ //
7753+ // apLocalPostorder only includes live assertions from prior statements.
7754+ //
7755+ if (fgGlobalMorph && optLocalAssertionProp && (optAssertionCount > 0))
7756+ {
7757+ GenTree* optimizedTree = tree;
7758+ bool again = JitConfig.JitEnablePostorderLocalAssertionProp() > 0;
7759+ bool didOptimize = false;
7760+
7761+ if (!again)
7762+ {
7763+ JITDUMP("*** Postorder assertion prop disabled by config\n");
7764+ }
7765+
7766+ while (again)
7767+ {
7768+ tree = optimizedTree;
7769+ optimizedTree = optAssertionProp(apLocalPostorder, tree, nullptr, nullptr);
7770+ again = (optimizedTree != nullptr);
7771+ didOptimize |= again;
7772+ }
7773+
7774+ assert(tree != nullptr);
7775+
7776+ if (didOptimize)
7777+ {
7778+ gtUpdateNodeSideEffects(tree);
7779+ }
7780+ }
7781+
77477782 // Try to fold it, maybe we get lucky,
77487783 tree = gtFoldExpr(tree);
77497784
@@ -11716,13 +11751,12 @@ void Compiler::fgKillDependentAssertionsSingle(unsigned lclNum DEBUGARG(GenTree*
1171611751{
1171711752 // Active dependent assertions are killed here
1171811753 //
11719- ASSERT_TP killed = BitVecOps::MakeCopy(apTraits, GetAssertionDep(lclNum));
11720- BitVecOps::IntersectionD(apTraits, killed, apLocal);
11721-
11722- if (killed)
11723- {
11754+ ASSERT_TP killed = GetAssertionDep(lclNum);
1172411755
1172511756#ifdef DEBUG
11757+ bool hasKills = !BitVecOps::IsEmptyIntersection(apTraits, apLocal, killed);
11758+ if (hasKills)
11759+ {
1172611760 AssertionIndex index = optAssertionCount;
1172711761 while (killed && (index > 0))
1172811762 {
@@ -11742,10 +11776,11 @@ void Compiler::fgKillDependentAssertionsSingle(unsigned lclNum DEBUGARG(GenTree*
1174211776
1174311777 index--;
1174411778 }
11779+ }
1174511780#endif
1174611781
11747- BitVecOps::DiffD(apTraits, apLocal, killed);
11748- }
11782+ BitVecOps::DiffD(apTraits, apLocal, killed);
11783+ BitVecOps::DiffD(apTraits, apLocalPostorder, killed);
1174911784}
1175011785
1175111786//------------------------------------------------------------------------
@@ -11762,7 +11797,7 @@ void Compiler::fgKillDependentAssertionsSingle(unsigned lclNum DEBUGARG(GenTree*
1176211797//
1176311798void Compiler::fgKillDependentAssertions(unsigned lclNum DEBUGARG(GenTree* tree))
1176411799{
11765- if (BitVecOps::IsEmpty(apTraits, apLocal))
11800+ if (BitVecOps::IsEmpty(apTraits, apLocal) && BitVecOps::IsEmpty(apTraits, apLocalPostorder) )
1176611801 {
1176711802 return;
1176811803 }
@@ -12448,6 +12483,11 @@ void Compiler::fgMorphStmts(BasicBlock* block)
1244812483 compCurStmt = stmt;
1244912484 GenTree* oldTree = stmt->GetRootNode();
1245012485
12486+ if (optLocalAssertionProp)
12487+ {
12488+ BitVecOps::Assign(apTraits, apLocalPostorder, apLocal);
12489+ }
12490+
1245112491#ifdef DEBUG
1245212492
1245312493 unsigned oldHash = verbose ? gtHashValue(oldTree) : DUMMY_INIT(~0);
@@ -12668,7 +12708,8 @@ void Compiler::fgMorphBlock(BasicBlock* block, MorphUnreachableInfo* unreachable
1266812708 // Each block starts with an empty table, and no available assertions
1266912709 //
1267012710 optAssertionReset(0);
12671- apLocal = BitVecOps::MakeEmpty(apTraits);
12711+ BitVecOps::ClearD(apTraits, apLocal);
12712+ BitVecOps::ClearD(apTraits, apLocalPostorder);
1267212713 }
1267312714 else
1267412715 {
@@ -12806,6 +12847,8 @@ void Compiler::fgMorphBlock(BasicBlock* block, MorphUnreachableInfo* unreachable
1280612847 apLocal = BitVecOps::MakeEmpty(apTraits);
1280712848 }
1280812849
12850+ BitVecOps::Assign(apTraits, apLocalPostorder, apLocal);
12851+
1280912852 JITDUMPEXEC(optDumpAssertionIndices("Assertions in: ", apLocal));
1281012853 }
1281112854 }
@@ -12874,6 +12917,8 @@ PhaseStatus Compiler::fgMorphBlocks()
1287412917 // Local assertion prop is enabled if we are optimizing.
1287512918 //
1287612919 optAssertionInit(/* isLocalProp*/ true);
12920+ apLocal = BitVecOps::MakeEmpty(apTraits);
12921+ apLocalPostorder = BitVecOps::MakeEmpty(apTraits);
1287712922 }
1287812923 else
1287912924 {
@@ -13009,10 +13054,12 @@ PhaseStatus Compiler::fgMorphBlocks()
1300913054
1301013055 if (optLocalAssertionProp)
1301113056 {
13012- Metrics.LocalAssertionCount = optAssertionCount;
13013- Metrics.LocalAssertionOverflow = optAssertionOverflow;
13014- Metrics.MorphTrackedLocals = lvaTrackedCount;
13015- Metrics.MorphLocals = lvaCount;
13057+ Metrics.LocalAssertionCount = optAssertionCount;
13058+ Metrics.LocalAssertionOverflow = optAssertionOverflow;
13059+ Metrics.MorphTrackedLocals = lvaTrackedCount;
13060+ Metrics.MorphLocals = lvaCount;
13061+ optLocalAssertionProp = false;
13062+ optCrossBlockLocalAssertionProp = false;
1301613063 }
1301713064
1301813065 // We may have converted a tailcall into a loop, in which case the first BB
0 commit comments