-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
JIT: build basic block pred lists before morph #1309
Changes from 7 commits
4b2ee6b
300c1df
10257d8
b21a56d
18f1ae1
e1ca6b7
30d6c9d
eee522a
620035a
c966815
d04dbff
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 |
---|---|---|
|
@@ -2729,13 +2729,31 @@ inline unsigned Compiler::fgThrowHlpBlkStkLevel(BasicBlock* block) | |
*/ | ||
inline void Compiler::fgConvertBBToThrowBB(BasicBlock* block) | ||
{ | ||
// If we're converting a BBJ_CALLFINALLY block to a BBJ_THROW block, | ||
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. Comment not related to your change: the size of some of these "inline" functions has gotten ridiculous. Seems like we should get rid of the ".hpp" file entirely and assume a good compiler will inline appropriately. 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. Agree, was going to move this one in particular, but perhaps they all should go. Perhaps part of some broader breakup of the bigger files into related sets of functionality or something. |
||
JITDUMP("Converting " FMT_BB " to BBJ_THROW\n", block->bbNum); | ||
|
||
// Ordering of the following operations matters. | ||
// First, note if we are looking at the first block of a call always pair. | ||
const bool isCallAlwaysPair = block->isBBCallAlwaysPair(); | ||
|
||
// Scrub this block from the pred lists of any successors | ||
fgRemoveBlockAsPred(block); | ||
|
||
// Update jump kind after the scrub. | ||
block->bbJumpKind = BBJ_THROW; | ||
|
||
// Any block with a throw is rare | ||
block->bbSetRunRarely(); | ||
|
||
// If we've converted a BBJ_CALLFINALLY block to a BBJ_THROW block, | ||
// then mark the subsequent BBJ_ALWAYS block as unreferenced. | ||
if (block->isBBCallAlwaysPair()) | ||
// | ||
// Must do this after we update bbJumpKind of block. | ||
if (isCallAlwaysPair) | ||
{ | ||
BasicBlock* leaveBlk = block->bbNext; | ||
noway_assert(leaveBlk->bbJumpKind == BBJ_ALWAYS); | ||
|
||
// leaveBlk is now unreachable, so scrub the pred lists. | ||
leaveBlk->bbFlags &= ~BBF_DONT_REMOVE; | ||
leaveBlk->bbRefs = 0; | ||
leaveBlk->bbPreds = nullptr; | ||
|
@@ -2757,9 +2775,6 @@ inline void Compiler::fgConvertBBToThrowBB(BasicBlock* block) | |
} | ||
#endif // defined(FEATURE_EH_FUNCLETS) && defined(_TARGET_ARM_) | ||
} | ||
|
||
block->bbJumpKind = BBJ_THROW; | ||
block->bbSetRunRarely(); // any block with a throw is rare | ||
} | ||
|
||
/***************************************************************************** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -431,9 +431,6 @@ BasicBlock* Compiler::fgNewBasicBlock(BBjumpKinds jumpKind) | |
|
||
void Compiler::fgEnsureFirstBBisScratch() | ||
{ | ||
// This method does not update predecessor lists and so must only be called before they are computed. | ||
assert(!fgComputePredsDone); | ||
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. Can this function now be called both before and after preds are computed? Or only after? Doesn't preds computation set bbRefs, and you assert on that, so it has to be after? In which case, could you 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. It can be called both before and after.
|
||
|
||
// Have we already allocated a scratch block? | ||
|
||
if (fgFirstBBisScratch()) | ||
|
@@ -453,6 +450,15 @@ void Compiler::fgEnsureFirstBBisScratch() | |
block->inheritWeight(fgFirstBB); | ||
} | ||
|
||
// The first block has an implicit ref count which we must | ||
// remove. Note the ref count could be greater that one, if | ||
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. grammar-o: that => than |
||
// the first block is not scratch and is targeted by a | ||
// branch. | ||
assert(fgFirstBB->bbRefs >= 1); | ||
fgFirstBB->bbRefs--; | ||
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. Since you're here, it works be nice to update the header, including when and under what pre-conditions it will be called. 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. Will do. |
||
|
||
// The new scratch bb will fall through to the old first bb | ||
fgAddRefPred(fgFirstBB, block); | ||
fgInsertBBbefore(fgFirstBB, block); | ||
} | ||
else | ||
|
@@ -466,6 +472,9 @@ void Compiler::fgEnsureFirstBBisScratch() | |
|
||
block->bbFlags |= (BBF_INTERNAL | BBF_IMPORTED); | ||
|
||
// This new first BB has an implicit ref, and no others. | ||
block->bbRefs = 1; | ||
|
||
fgFirstBBScratch = fgFirstBB; | ||
|
||
#ifdef DEBUG | ||
|
@@ -8244,7 +8253,7 @@ void Compiler::fgConvertSyncReturnToLeave(BasicBlock* block) | |
// Convert the BBJ_RETURN to BBJ_ALWAYS, jumping to genReturnBB. | ||
block->bbJumpKind = BBJ_ALWAYS; | ||
block->bbJumpDest = genReturnBB; | ||
block->bbJumpDest->bbRefs++; | ||
fgAddRefPred(genReturnBB, block); | ||
|
||
#ifdef DEBUG | ||
if (verbose) | ||
|
@@ -8531,7 +8540,7 @@ class MergedReturns | |
newReturnBB->bbRefs = 1; // bbRefs gets update later, for now it should be 1 | ||
comp->fgReturnCount++; | ||
|
||
newReturnBB->bbFlags |= BBF_INTERNAL; | ||
newReturnBB->bbFlags |= (BBF_INTERNAL | BBF_JMP_TARGET); | ||
|
||
noway_assert(newReturnBB->bbNext == nullptr); | ||
|
||
|
@@ -13102,6 +13111,7 @@ void Compiler::fgComputeBlockAndEdgeWeights() | |
const bool usingProfileWeights = fgIsUsingProfileWeights(); | ||
const bool isOptimizing = opts.OptimizationEnabled(); | ||
|
||
fgModified = false; | ||
fgHaveValidEdgeWeights = false; | ||
fgCalledCount = BB_UNITY_WEIGHT; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5483,6 +5483,18 @@ bool GenTree::OperMayThrow(Compiler* comp) | |
case GT_ARR_ELEM: | ||
return comp->fgAddrCouldBeNull(this->AsArrElem()->gtArrObj); | ||
|
||
case GT_FIELD: | ||
{ | ||
GenTree* fldObj = this->AsField()->gtFldObj; | ||
|
||
if (fldObj != nullptr) | ||
{ | ||
return comp->fgAddrCouldBeNull(fldObj); | ||
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. This was presumably needed because this is now called before morph has removed these? 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. Yes. |
||
} | ||
|
||
return false; | ||
} | ||
|
||
case GT_ARR_BOUNDS_CHECK: | ||
case GT_ARR_INDEX: | ||
case GT_ARR_OFFSET: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7232,6 +7232,11 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call) | |
} | ||
#endif | ||
|
||
// This block is not longer any block's predecessor. If we end up | ||
// converting this tail call to a branch, we'll add appropriate | ||
// successor information then. | ||
fgRemoveBlockAsPred(compCurBB); | ||
|
||
#if !FEATURE_TAILCALL_OPT_SHARED_RETURN | ||
// We enable shared-ret tail call optimization for recursive calls even if | ||
// FEATURE_TAILCALL_OPT_SHARED_RETURN is not defined. | ||
|
@@ -8033,6 +8038,7 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa | |
fgFirstBB->bbFlags |= BBF_DONT_REMOVE; | ||
block->bbJumpKind = BBJ_ALWAYS; | ||
block->bbJumpDest = fgFirstBB->bbNext; | ||
block->bbJumpDest->bbFlags |= BBF_JMP_TARGET; | ||
fgAddRefPred(block->bbJumpDest, block); | ||
block->bbFlags &= ~BBF_HAS_JMP; | ||
} | ||
|
@@ -14877,12 +14883,6 @@ bool Compiler::fgFoldConditional(BasicBlock* block) | |
/* Unconditional throw - transform the basic block into a BBJ_THROW */ | ||
fgConvertBBToThrowBB(block); | ||
|
||
/* Remove 'block' from the predecessor list of 'block->bbNext' */ | ||
fgRemoveRefPred(block->bbNext, block); | ||
|
||
/* Remove 'block' from the predecessor list of 'block->bbJumpDest' */ | ||
fgRemoveRefPred(block->bbJumpDest, block); | ||
|
||
#ifdef DEBUG | ||
if (verbose) | ||
{ | ||
|
@@ -15096,19 +15096,6 @@ bool Compiler::fgFoldConditional(BasicBlock* block) | |
/* Unconditional throw - transform the basic block into a BBJ_THROW */ | ||
fgConvertBBToThrowBB(block); | ||
|
||
/* update the flow graph */ | ||
|
||
unsigned jumpCnt = block->bbJumpSwt->bbsCount; | ||
BasicBlock** jumpTab = block->bbJumpSwt->bbsDstTab; | ||
|
||
for (unsigned val = 0; val < jumpCnt; val++, jumpTab++) | ||
{ | ||
BasicBlock* curJump = *jumpTab; | ||
|
||
/* Remove 'block' from the predecessor list of 'curJump' */ | ||
fgRemoveRefPred(curJump, block); | ||
} | ||
|
||
#ifdef DEBUG | ||
if (verbose) | ||
{ | ||
|
@@ -15207,22 +15194,30 @@ bool Compiler::fgFoldConditional(BasicBlock* block) | |
return result; | ||
} | ||
|
||
//***************************************************************************** | ||
//------------------------------------------------------------------------ | ||
// fgMorphBlockStmt: morph a single statement in a block. | ||
// | ||
// Morphs a single statement in a block. | ||
// Can be called anytime, unlike fgMorphStmts() which should only be called once. | ||
// Arguments: | ||
// block - block containing the statement | ||
// stmt - statement to morph | ||
// msg - string to identify caller in a dump | ||
// | ||
// Returns true if 'stmt' was removed from the block. | ||
// Returns false if 'stmt' is still in the block (even if other statements were removed). | ||
// Returns: | ||
// true if 'stmt' was removed from the block. | ||
// s false if 'stmt' is still in the block (even if other statements were removed). | ||
// | ||
// Notes: | ||
// Can be called anytime, unlike fgMorphStmts() which should only be called once. | ||
// | ||
|
||
bool Compiler::fgMorphBlockStmt(BasicBlock* block, Statement* stmt DEBUGARG(const char* msg)) | ||
{ | ||
assert(block != nullptr); | ||
assert(stmt != nullptr); | ||
|
||
compCurBB = block; | ||
compCurStmt = stmt; | ||
// Reset some ambient state | ||
fgRemoveRestOfBlock = false; | ||
compCurBB = block; | ||
compCurStmt = stmt; | ||
|
||
GenTree* morph = fgMorphTree(stmt->GetRootNode()); | ||
|
||
|
@@ -15315,10 +15310,7 @@ bool Compiler::fgMorphBlockStmt(BasicBlock* block, Statement* stmt DEBUGARG(cons | |
} | ||
|
||
// The rest of block has been removed and we will always throw an exception. | ||
|
||
// Update succesors of block | ||
fgRemoveBlockAsPred(block); | ||
|
||
// | ||
// For compDbgCode, we prepend an empty BB as the firstBB, it is BBJ_NONE. | ||
// We should not convert it to a ThrowBB. | ||
if ((block != fgFirstBB) || ((fgFirstBB->bbFlags & BBF_INTERNAL) == 0)) | ||
|
@@ -15682,6 +15674,7 @@ void Compiler::fgMorphBlocks() | |
{ | ||
block->bbJumpKind = BBJ_ALWAYS; | ||
block->bbJumpDest = genReturnBB; | ||
fgAddRefPred(genReturnBB, block); | ||
fgReturnCount--; | ||
} | ||
if (genReturnLocal != BAD_VAR_NUM) | ||
|
@@ -16642,6 +16635,25 @@ void Compiler::fgMorph() | |
|
||
EndPhase(PHASE_CLONE_FINALLY); | ||
|
||
/* Compute bbNum, bbRefs and bbPreds */ | ||
|
||
JITDUMP("\nRenumbering the basic blocks for fgComputePreds\n"); | ||
fgRenumberBlocks(); | ||
|
||
noway_assert(!fgComputePredsDone); // This is the first time full (not cheap) preds will be computed. | ||
fgComputePreds(); | ||
|
||
// Run an early flow graph simplification pass | ||
if (opts.OptimizationEnabled()) | ||
{ | ||
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. I realize this was just cut-and-pasted, but it would be nice to update the comment style. 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. Sure. FWIW there are a lot of old-style comments throughout 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. That sounds good. |
||
fgUpdateFlowGraph(); | ||
} | ||
|
||
EndPhase(PHASE_COMPUTE_PREDS); | ||
|
||
/* From this point on the flowgraph information such as bbNum, | ||
* bbRefs or bbPreds has to be kept updated */ | ||
|
||
/* For x64 and ARM64 we need to mark irregular parameters */ | ||
lvaRefCountState = RCS_EARLY; | ||
fgResetImplicitByRefRefCount(); | ||
|
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.
Unrelated change?
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.
No, this is a necessary fix, otherwise we fold handle + const into an LEA on x86, and lose track of a reloc.
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 see; so it was an existing issue that was exposed by this change?
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.
Sort of ... looking at this again perhaps I should fix it differently.
VN-based constant prop won't propagate handles for 32 bit targets when
opts.compReloc
is true. But assertion-based constant prop doesn't have this restriction. And so with this change, the early flow opts enable an assertion-based constant prop of a handle, and on x86 constant handle is fodder for LEA formation.So maybe I should update
optConstantAssertionProp
with similar restrictions as vn based const prop and revert the codegen change (or turn it into an assert).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 think I will split this part of as a separate change, which should be zero-diff and will allow bit more refactoring than I'd do if I tried folding it in here. Should have that PR up soon.