Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
2dcd5e4
Expand runtime lookups in a late phase
EgorBo Feb 4, 2023
2c7a4a3
Clean up
EgorBo Feb 4, 2023
5754331
Merge branch 'main' of github.com:dotnet/runtime into expand-runtime-…
EgorBo Feb 5, 2023
dca8140
test
EgorBo Feb 6, 2023
e303869
test2
EgorBo Feb 6, 2023
aa760e5
Merge branch 'main' of github.com:dotnet/runtime into expand-runtime-…
EgorBo Feb 25, 2023
20e79dc
Test
EgorBo Feb 25, 2023
22f34a1
Enable JitDasmWithAlignmentBoundaries in Release
EgorBo Feb 25, 2023
c3681c1
Merge branch 'main' of github.com:dotnet/runtime into expand-runtime-…
EgorBo Feb 25, 2023
2110ec0
clean up
EgorBo Feb 25, 2023
54b224c
Revert "Enable JitDasmWithAlignmentBoundaries in Release"
EgorBo Feb 25, 2023
e4d4944
add hashtable
EgorBo Feb 25, 2023
3e272e0
test
EgorBo Feb 25, 2023
2671e14
Initial version (no dynamic expansion for now)
EgorBo Feb 25, 2023
64ae149
Clean up
EgorBo Feb 25, 2023
d991533
Clean up
EgorBo Feb 26, 2023
e20dd2d
Test
EgorBo Feb 26, 2023
8fc0374
Update BB flags properly
EgorBo Feb 26, 2023
269cb36
Add dynamic expansion path
EgorBo Feb 26, 2023
3c1cb1c
Add dynamic expansion
EgorBo Feb 26, 2023
47524ed
Add comments
EgorBo Feb 26, 2023
05ae42d
Merge branch 'main' of github.com:dotnet/runtime into expand-runtime-…
EgorBo Feb 28, 2023
ba8fdc2
test
EgorBo Feb 28, 2023
5d6cf4a
test #2
EgorBo Feb 28, 2023
ec00f5b
it should be <=
EgorBo Feb 28, 2023
ec893da
Update flowgraph.cpp
EgorBo Feb 28, 2023
f81882f
test 3
EgorBo Feb 28, 2023
d1d0bc8
test 4
EgorBo Mar 1, 2023
eea468a
test 4
EgorBo Mar 1, 2023
3d5d61a
Merge branch 'main' of github.com:dotnet/runtime into expand-runtime-…
EgorBo Mar 2, 2023
9af1079
Test 5
EgorBo Mar 2, 2023
ea3bf76
Clean up
EgorBo Mar 2, 2023
8a2a8ad
Found it!
EgorBo Mar 2, 2023
777def6
fix assert
EgorBo Mar 3, 2023
b18b12e
fix assert
EgorBo Mar 3, 2023
0f7fc3d
Merge branch 'main' of github.com:dotnet/runtime into expand-runtime-…
EgorBo Mar 3, 2023
5eee49b
move to separate file + fix some diff regressions
EgorBo Mar 3, 2023
2ff3799
Clean up, address some of the feedback
EgorBo Mar 3, 2023
11cac11
Address feedback [WIP]
EgorBo Mar 3, 2023
936c7d2
Implement gtSplitTree
jakobbotsch Mar 4, 2023
a982e68
Test
EgorBo Mar 4, 2023
aad1188
fix Release
EgorBo Mar 4, 2023
f3853a7
Fix assert
EgorBo Mar 4, 2023
f2d22fe
Initial clean up
EgorBo Mar 5, 2023
3e3b15f
Mark ArrayStack ctor explicit
jakobbotsch Mar 5, 2023
0f40259
Add docs to gtSplitTree
jakobbotsch Mar 5, 2023
2a29ac3
Fix up ASG(COMMA(...), ...) handling
jakobbotsch Mar 5, 2023
598ff71
Split out uses in execution order instead
jakobbotsch Mar 5, 2023
733fcd9
Remove dead local
jakobbotsch Mar 5, 2023
a62a135
Clean up
EgorBo Mar 5, 2023
730ed35
Avoid creating statements for non address-exposed locals
jakobbotsch Mar 5, 2023
9c6c89f
More cleanup (use callUse)
EgorBo Mar 5, 2023
f2c1508
use old path for tier0 (slightly better CQ/TP for tier0)
EgorBo Mar 5, 2023
c1cc980
Code clean up
EgorBo Mar 5, 2023
6f587df
Merge branch 'main' of github.com:dotnet/runtime into expand-runtime-…
EgorBo Mar 11, 2023
2834318
resolve conflicts
EgorBo Mar 11, 2023
79b5076
Update runtimelookup.cpp
EgorBo Mar 12, 2023
f0f14a7
Update src/coreclr/jit/runtimelookup.cpp
EgorBo Mar 12, 2023
6061fa7
Merge branch 'main' of github.com:dotnet/runtime into expand-runtime-…
EgorBo Mar 13, 2023
2a3d83b
Mitigate some tier0 regressions
EgorBo Mar 13, 2023
07cf9e7
clone fastpath for tier0
EgorBo Mar 13, 2023
3a3f4ca
Remove redundant impSpillSideEffects
EgorBo Mar 13, 2023
8af93a9
test
EgorBo Mar 14, 2023
afe2dd4
Fix regressions
EgorBo Mar 14, 2023
7909435
fix tp regressions
EgorBo Mar 14, 2023
258cbae
Apply suggestions from code review
EgorBo Mar 14, 2023
1afb6f8
Address feedback
EgorBo Mar 14, 2023
a116695
Add goto SCAN_BLOCK_AGAIN
EgorBo Mar 14, 2023
386446e
Update src/coreclr/jit/runtimelookup.cpp
EgorBo Mar 14, 2023
d2f3fec
Address feedback
EgorBo Mar 14, 2023
19034d9
update side effects for fallbackBb
EgorBo Mar 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add dynamic expansion path
  • Loading branch information
EgorBo committed Feb 26, 2023
commit 269cb366b7b1ca5dde688a963d4fbe6eb6521d3c
168 changes: 132 additions & 36 deletions src/coreclr/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,21 +142,23 @@ PhaseStatus Compiler::fgExpandRuntimeLookups()
fgMakeMultiUse(&ctxTree);
}

// Prepare slotPtr tree
GenTree* slotPtrTree = ctxTree;
GenTree* indOffTree = nullptr;
// Prepare slotPtr tree (TODO: consider sharing this part with impRuntimeLookup)
GenTree* slotPtrTree = ctxTree;
GenTree* indOffTree = nullptr;
GenTree* lastIndOfTree = nullptr;
for (WORD i = 0; i < runtimeLookup.indirections; i++)
{
if ((i == 1 && runtimeLookup.indirectFirstOffset) || (i == 2 && runtimeLookup.indirectSecondOffset))
{
indOffTree = fgMakeMultiUse(&slotPtrTree);
}

// The last indirection could be subject to a size check (dynamic dictionary expansion)
bool isLastIndirectionWithSizeCheck = ((i == runtimeLookup.indirections - 1) &&
(runtimeLookup.sizeOffset != CORINFO_NO_SIZE_CHECK));

if (i != 0)
{
// The last indirection could be subject to a size check (dynamic dictionary expansion)
bool isLastIndirectionWithSizeCheck = ((i == runtimeLookup.indirections - 1) &&
(runtimeLookup.sizeOffset != CORINFO_NO_SIZE_CHECK));

slotPtrTree = gtNewOperNode(GT_IND, TYP_I_IMPL, slotPtrTree);
slotPtrTree->gtFlags |= GTF_IND_NONFAULTING;
Expand All @@ -173,6 +175,12 @@ PhaseStatus Compiler::fgExpandRuntimeLookups()

if (runtimeLookup.offsets[i] != 0)
{
if (isLastIndirectionWithSizeCheck)
{
lastIndOfTree = impCloneExpr(slotPtrTree, &slotPtrTree, NO_CLASS_HANDLE, CHECK_SPILL_ALL,
nullptr DEBUGARG("impRuntimeLookup indirectOffset"));
}

slotPtrTree = gtNewOperNode(GT_ADD, TYP_I_IMPL, slotPtrTree,
gtNewIconNode(runtimeLookup.offsets[i], TYP_I_IMPL));
}
Expand All @@ -188,22 +196,23 @@ PhaseStatus Compiler::fgExpandRuntimeLookups()
block->bbFlags |= originalFlags & (BBF_SPLIT_GAINED | BBF_IMPORTED | BBF_GC_SAFE_POINT |
BBF_LOOP_PREHEADER | BBF_RETLESS_CALL);

// Non-dynamic expansion case (no size check):
//
// prevBb(BBJ_NONE):
// prevBb(BBJ_NONE): [weight: 1.0]
// ...
//
// nullcheckBb(BBJ_COND):
// if (fastPathValue != 0)
// goto fastPathBb;
// nullcheckBb(BBJ_COND): [weight: 1.0]
// if (fastPathValue == 0)
// goto fallbackBb;
//
// fallbackBb(BBJ_ALWAYS):
// rtLookupLcl = HelperCall();
// fastPathBb(BBJ_ALWAYS): [weight: 0.8]
// rtLookupLcl = fastPathValue;
// goto block;
//
// fastPathBb(BBJ_NONE):
// rtLookupLcl = fastPathValue;
// fallbackBb(BBJ_NONE): [weight: 0.2]
// rtLookupLcl = HelperCall();
//
// block(...):
// block(...): [weight: 1.0]
// use(rtLookupLcl);
//

Expand All @@ -215,19 +224,22 @@ PhaseStatus Compiler::fgExpandRuntimeLookups()
fastPathValue->gtFlags |= GTF_IND_NONFAULTING;
GenTree* fastPathValueClone = fgMakeMultiUse(&fastPathValue);

GenTree* nullcheckOp = gtNewOperNode(GT_NE, TYP_INT, fastPathValue, gtNewIconNode(0, TYP_I_IMPL));
GenTree* nullcheckOp = gtNewOperNode(GT_EQ, TYP_INT, fastPathValue, gtNewIconNode(0, TYP_I_IMPL));
nullcheckOp->gtFlags |= GTF_RELOP_JMP_USED;
gtSetEvalOrder(nullcheckOp);
Statement* nullcheckStmt = fgNewStmtFromTree(gtNewOperNode(GT_JTRUE, TYP_VOID, nullcheckOp));
gtSetStmtInfo(nullcheckStmt);
fgSetStmtSeq(nullcheckStmt);
fgInsertStmtAtEnd(nullcheckBb, nullcheckStmt);

BasicBlock* sizeCheckBb = nullptr;
if (needsSizeCheck)
{
// TODO:
}
// Fallback basic block
BasicBlock* fallbackBb = fgNewBBafter(BBJ_NONE, nullcheckBb, true);
fallbackBb->bbFlags |= BBF_INTERNAL;
Statement* asgFallbackStmt =
fgNewStmtFromTree(gtNewAssignNode(gtClone(rtLookupLcl), gtCloneExpr(call)));
fgInsertStmtAtBeg(fallbackBb, asgFallbackStmt);
gtSetStmtInfo(asgFallbackStmt);
fgSetStmtSeq(asgFallbackStmt);

// Fast-path basic block
BasicBlock* fastPathBb = fgNewBBafter(BBJ_ALWAYS, nullcheckBb, true);
Expand All @@ -238,14 +250,53 @@ PhaseStatus Compiler::fgExpandRuntimeLookups()
gtSetStmtInfo(asgFastPathValueStmt);
fgSetStmtSeq(asgFastPathValueStmt);

// Fallback basic block
BasicBlock* fallbackBb = fgNewBBafter(BBJ_ALWAYS, nullcheckBb, true);
fallbackBb->bbFlags |= BBF_INTERNAL;
Statement* asgFallbackStmt =
fgNewStmtFromTree(gtNewAssignNode(gtClone(rtLookupLcl), gtCloneExpr(call)));
fgInsertStmtAtBeg(fallbackBb, asgFallbackStmt);
gtSetStmtInfo(asgFallbackStmt);
fgSetStmtSeq(asgFallbackStmt);
BasicBlock* sizeCheckBb = nullptr;
if (needsSizeCheck)
{
// Dynamic expansion case (sizeCheckBb is added and some preds are changed):
//
// prevBb(BBJ_NONE): [weight: 1.0]
// ...
//
// nullcheckBb(BBJ_COND): [weight: 1.0]
// if (fastPathValue == 0)
// goto fallbackBb;
//
// sizeCheckBb(BBJ_COND): [weight: 0.8]
// if (fastPathValue == 0)
// goto fallbackBb;
//
// fastPathBb(BBJ_ALWAYS): [weight: 0.64]
// rtLookupLcl = fastPathValue;
// goto block;
//
// fallbackBb(BBJ_NONE): [weight: 0.36]
// rtLookupLcl = HelperCall();
//
// block(...): [weight: 1.0]
// use(rtLookupLcl);
//

sizeCheckBb = fgNewBBafter(BBJ_COND, nullcheckBb, true);
sizeCheckBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_JMP);

// sizeValue = dictionary[pRuntimeLookup->sizeOffset]
GenTreeIntCon* sizeOffset = gtNewIconNode(runtimeLookup.sizeOffset, TYP_I_IMPL);
assert(lastIndOfTree != nullptr);
GenTree* sizeValueOffset = gtNewOperNode(GT_ADD, TYP_I_IMPL, lastIndOfTree, sizeOffset);
GenTree* sizeValue = gtNewOperNode(GT_IND, TYP_I_IMPL, sizeValueOffset);
sizeValue->gtFlags |= GTF_IND_NONFAULTING;

// sizeCheck fails if sizeValue < pRuntimeLookup->offsets[i]
GenTree* offsetValue = gtNewIconNode(runtimeLookup.offsets[runtimeLookup.indirections - 1], TYP_I_IMPL);
GenTree* sizeCheck = gtNewOperNode(GT_LT, TYP_INT, sizeValue, offsetValue);
sizeCheck->gtFlags |= GTF_RELOP_JMP_USED;
gtSetEvalOrder(sizeCheck);
Statement* sizeCheckStmt = fgNewStmtFromTree(gtNewOperNode(GT_JTRUE, TYP_VOID, sizeCheck));
gtSetStmtInfo(sizeCheckStmt);
fgSetStmtSeq(sizeCheckStmt);
fgInsertStmtAtEnd(sizeCheckBb, sizeCheckStmt);
}

// Replace call with rtLookupLclNum local
call->ReplaceWith(gtNewLclvNode(rtLookupLclNum, call->TypeGet()), this);
Expand All @@ -255,25 +306,70 @@ PhaseStatus Compiler::fgExpandRuntimeLookups()

// Connect all new blocks together
fgAddRefPred(nullcheckBb, prevBb);
fgAddRefPred(fallbackBb, nullcheckBb);
fgAddRefPred(fastPathBb, nullcheckBb);
fgRemoveRefPred(block, prevBb);
fgAddRefPred(block, fastPathBb);
fgAddRefPred(block, fallbackBb);
nullcheckBb->bbJumpDest = fastPathBb;
nullcheckBb->bbJumpDest = fallbackBb;
fastPathBb->bbJumpDest = block;
fallbackBb->bbJumpDest = block;

// Re-distribute weights
if (needsSizeCheck)
{
// nullcheckBb flows into sizeCheckBb in case of non-null
fgAddRefPred(sizeCheckBb, nullcheckBb);

// fallbackBb is reachable from either nullcheck or sizecheck
fgAddRefPred(fallbackBb, nullcheckBb);
fgAddRefPred(fallbackBb, sizeCheckBb);

// fastPathBb is only reachable from successful sizeCheckBb
fgAddRefPred(fastPathBb, sizeCheckBb);

// sizeCheckBb fails - jump to fallbackBb
sizeCheckBb->bbJumpDest = fallbackBb;
}
else
{
// No size check, nullcheckBb jumps to fast path
fgAddRefPred(fastPathBb, nullcheckBb);

// fallbackBb is only reachable from nullcheckBb (jump destination)
fgAddRefPred(fallbackBb, nullcheckBb);
}

// Re-distribute weights (see '[weight: X]' on the diagrams above)

// First, nullcheck and the last block are expected to just inherit prevBb weight
nullcheckBb->inheritWeight(prevBb);
fallbackBb->inheritWeightPercentage(nullcheckBb, 20); // TODO: Consider making it cold (0%)
fastPathBb->inheritWeightPercentage(nullcheckBb, 80);
block->inheritWeight(prevBb);

if (needsSizeCheck)
{
// 80% chance we pass nullcheck
sizeCheckBb->inheritWeightPercentage(nullcheckBb, 80);

// 64% (0.8 * 0.8) chance we pass both nullcheck and sizecheck
fastPathBb->inheritWeightPercentage(sizeCheckBb, 80);

// 100-64=36% chance we fail either nullcheck or sizecheck
fallbackBb->inheritWeightPercentage(nullcheckBb, 36);
}
else
{
// 80% chance we pass nullcheck
fastPathBb->inheritWeightPercentage(nullcheckBb, 80);

// 20% chance we fail nullcheck (TODO: Consider making it cold (0%))
fallbackBb->inheritWeightPercentage(nullcheckBb, 20);
}

// All blocks are expected to be in the same EH region
assert(BasicBlock::sameEHRegion(prevBb, block));
assert(BasicBlock::sameEHRegion(prevBb, nullcheckBb));
assert(BasicBlock::sameEHRegion(prevBb, fastPathBb));
if (needsSizeCheck)
{
assert(BasicBlock::sameEHRegion(prevBb, sizeCheckBb));
}

// Scan current block again, the current call will be ignored because of ClearExpRuntimeLookup.
// We don't try to re-use expansions for the same lookups in the current block here - CSE is responsible
Expand Down