-
Notifications
You must be signed in to change notification settings - Fork 5.1k
JIT: cross-block local assertion prop in morph #94363
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ad42b51
cceb9f8
bc59375
2b78bdc
8fa2318
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13780,10 +13780,73 @@ void Compiler::fgMorphBlock(BasicBlock* block) | |
|
||
if (optLocalAssertionProp) | ||
{ | ||
// For now, each block starts with an empty table, and no available assertions | ||
// | ||
optAssertionReset(0); | ||
apLocal = BitVecOps::MakeEmpty(apTraits); | ||
if (!optCrossBlockLocalAssertionProp) | ||
{ | ||
// Each block starts with an empty table, and no available assertions | ||
// | ||
optAssertionReset(0); | ||
apLocal = BitVecOps::MakeEmpty(apTraits); | ||
} | ||
else | ||
{ | ||
// 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); | ||
|
||
// Validate all preds have valid info | ||
// | ||
if (!canUsePredAssertions) | ||
{ | ||
JITDUMP(FMT_BB " ineligible for cross-block\n", block->bbNum); | ||
} | ||
else | ||
{ | ||
bool hasPredAssertions = false; | ||
|
||
for (BasicBlock* const pred : block->PredBlocks()) | ||
{ | ||
// 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) | ||
{ | ||
JITDUMP(FMT_BB " pred " FMT_BB " not processed; clearing assertions in\n", block->bbNum, | ||
pred->bbNum); | ||
break; | ||
} | ||
|
||
// Yes, pred assertions are available. If this is the first pred, copy. | ||
// If this is a subsequent pred, intersect. | ||
// | ||
if (!hasPredAssertions) | ||
{ | ||
apLocal = BitVecOps::MakeCopy(apTraits, pred->bbAssertionOut); | ||
hasPredAssertions = true; | ||
} | ||
else | ||
{ | ||
BitVecOps::IntersectionD(apTraits, apLocal, pred->bbAssertionOut); | ||
} | ||
} | ||
|
||
if (!hasPredAssertions) | ||
{ | ||
// Either no preds, or some preds w/o assertions. | ||
// | ||
canUsePredAssertions = false; | ||
} | ||
} | ||
|
||
if (!canUsePredAssertions) | ||
{ | ||
apLocal = BitVecOps::MakeEmpty(apTraits); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this also need Can the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No. There are 3 modes of operation here:
Modes 2 and 3 can mix with each other, but not with mode 1. We use mode 1 if the feature is disabled, or if the method has a lot of locals. When the feature is enabled, and the method does not have a lot of locals, we use mode 2 (if
Let me look at tweaking this. |
||
} | ||
|
||
JITDUMPEXEC(optDumpAssertionIndices("Assertions in: ", apLocal)); | ||
} | ||
} | ||
|
||
// Make the current basic block address available globally. | ||
|
@@ -13801,6 +13864,14 @@ void Compiler::fgMorphBlock(BasicBlock* block) | |
} | ||
} | ||
|
||
// Publish the live out state. | ||
// | ||
if (optCrossBlockLocalAssertionProp && (block->NumSucc() > 0)) | ||
{ | ||
assert(optLocalAssertionProp); | ||
block->bbAssertionOut = BitVecOps::MakeCopy(apTraits, apLocal); | ||
} | ||
|
||
compCurBB = nullptr; | ||
} | ||
|
||
|
@@ -13820,15 +13891,18 @@ PhaseStatus Compiler::fgMorphBlocks() | |
// | ||
fgGlobalMorph = true; | ||
|
||
// Local assertion prop is enabled if we are optimized | ||
// | ||
optLocalAssertionProp = opts.OptimizationEnabled(); | ||
|
||
if (optLocalAssertionProp) | ||
if (opts.OptimizationEnabled()) | ||
{ | ||
// Local assertion prop is enabled if we are optimizing. | ||
// | ||
optAssertionInit(/* isLocalProp*/ true); | ||
} | ||
else | ||
{ | ||
// Initialize for local assertion prop | ||
// Not optimizing. No assertion prop. | ||
// | ||
optAssertionInit(true); | ||
optLocalAssertionProp = false; | ||
optCrossBlockLocalAssertionProp = false; | ||
} | ||
|
||
if (!compEnregLocals()) | ||
|
@@ -13855,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); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it might be very "spurious diff" inducing (add a local and suddenly a bunch of assertions stop being propagated). You might have mentioned it somewhere else, but do you have any plans to make this less "hard threshold-y"?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also looking at things like
optAssertionDep
below it kind of feels like we should be callinglvaSortByRefCount
here and utilize tracked indices, although I don't think ref counts are up to date here (in particular ones introduced by physical promotion -- might not be so hard to fix that).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it's one of those cases where it would be great to have separated "single block" locals and "global" locals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have any great ideas here, no. Some of this is the inherent cost of a blind forward scheme, we push facts along hoping they will matter. SSA would be helpful, we could just attach assertions to SSA lifetimes.
Some kind of prioritization scheme might work out nicely; presumably frequently referenced locals are more interesting candidates for assertions.
I had the same thought -- I tried an experiment to see how many assertions were killed in the same block they were born in, and it was quite small; most of them get killed at merges.
It might be possible to leverage RCS_EARLY to deduce if newly generated assertions are likely to be referenced later on; if not they could be purged before they "escape" the block. But we're also enabling cross-block copy prop so even getting that right might be tricky, as the ref counts can and will change during morph.