Skip to content

Commit 3c8bae3

Browse files
authored
JIT: also run local assertion prop in postorder during morph (dotnet#115626)
Morph currently applies assertions in preorder, so updates made by morph or by local assertion prop to child trees cannot be leveraged when considering parents. For instance if we had ``` V01 = newarr V05 = V01 if (V05 == null) ``` morph was unable to resolve the branch, despite generating both `V01 != null` and `V05 == V01`. To address this, keep track of the assertions valid at the end of each statement (via `apLocalPostorder`), and use those assertions during morph postorder to try and optimize further. This assertion set is kept up to date with any subsequent kills, and (for simplicity) cleared out if there is a qmark. Note morph can't use the current (`apLocal`) assertion set in postorder because morph may also reorder subtrees, so in postorder might end up using assertions generated by (formerly) later-executing subtrees to optimize now earlier-executing subtrees.
1 parent 0f0b4b4 commit 3c8bae3

File tree

3 files changed

+64
-13
lines changed

3 files changed

+64
-13
lines changed

src/coreclr/jit/compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7705,6 +7705,7 @@ class Compiler
77057705
BitVecTraits* apTraits;
77067706
ASSERT_TP apFull;
77077707
ASSERT_TP apLocal;
7708+
ASSERT_TP apLocalPostorder;
77087709
ASSERT_TP apLocalIfTrue;
77097710

77107711
enum optAssertionKind : uint8_t

src/coreclr/jit/jitconfigvalues.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,9 @@ RELEASE_CONFIG_INTEGER(JitEnablePhysicalPromotion, "JitEnablePhysicalPromotion",
808808
// Enable cross-block local assertion prop
809809
RELEASE_CONFIG_INTEGER(JitEnableCrossBlockLocalAssertionProp, "JitEnableCrossBlockLocalAssertionProp", 1)
810810

811+
// Enable postorder local assertion prop
812+
RELEASE_CONFIG_INTEGER(JitEnablePostorderLocalAssertionProp, "JitEnablePostorderLocalAssertionProp", 1)
813+
811814
// Enable strength reduction
812815
RELEASE_CONFIG_INTEGER(JitEnableStrengthReduction, "JitEnableStrengthReduction", 1)
813816

src/coreclr/jit/morph.cpp

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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
//
1176311798
void 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

Comments
 (0)