-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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: Add explicit block fallthrough successor #93772
Changes from all commits
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 |
---|---|---|
|
@@ -537,6 +537,8 @@ struct BasicBlock : private LIR::Range | |
BBehfDesc* bbJumpEhf; // BBJ_EHFINALLYRET descriptor | ||
}; | ||
|
||
BasicBlock* bbFallThroughSucc; | ||
|
||
public: | ||
#ifdef DEBUG | ||
// When creating a block with a jump, we require its jump kind and target be initialized simultaneously. | ||
|
@@ -581,7 +583,8 @@ struct BasicBlock : private LIR::Range | |
|
||
void SetNext(BasicBlock* next) | ||
{ | ||
bbNext = next; | ||
bbNext = next; | ||
bbFallThroughSucc = bbNext; | ||
if (next) | ||
{ | ||
next->bbPrev = this; | ||
|
@@ -666,6 +669,38 @@ struct BasicBlock : private LIR::Range | |
SetJumpKindAndTarget(jumpKind, jumpDest DEBUG_ARG(compiler)); | ||
} | ||
|
||
BasicBlock* GetFallThroughSucc() const | ||
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 is a bit of a confusing name. The fall-through successor is always going to be the next block. What may not be is the "false" branch of a 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, in the case of 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 would suggest
I wouldn't worry too much about the callfinally cases. Callfinally cases are inherently complex, one has to look up how they work specifically. Even if you make 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. I see, thanks for the suggestion! I'll change it. |
||
{ | ||
assert(bbFallsThrough()); | ||
// For now, bbFallThroughSucc cannot diverge from bbNext | ||
assert(FallsIntoNext()); | ||
return bbFallThroughSucc; | ||
} | ||
|
||
void SetFallThroughSucc(BasicBlock* fallThroughSucc) | ||
{ | ||
// TODO: Once we allow bbFallThroughSucc to diverge from bbNext, ensure we only set bbFallThroughSucc | ||
// to something other than bbNext when this block is a BBJ_COND. | ||
// For BBJ_CALLFINALLY blocks part of call-always pairs, bbFallThroughSucc points to the BBJ_ALWAYS block. | ||
// For now, call-always pairs are always contiguous, so bbFallThroughSucc cannot diverge from bbNext. | ||
assert(KindIs(BBJ_COND)); | ||
bbFallThroughSucc = fallThroughSucc; | ||
// For now, bbFallThroughSucc cannot diverge from bbNext | ||
assert(FallsIntoNext()); | ||
} | ||
|
||
bool FallsInto(const BasicBlock* dest) const | ||
{ | ||
assert(bbFallsThrough()); | ||
return (bbFallThroughSucc == dest); | ||
} | ||
|
||
bool FallsIntoNext() const | ||
{ | ||
assert(bbFallsThrough()); | ||
return (bbFallThroughSucc == bbNext); | ||
} | ||
|
||
bool HasJump() const | ||
{ | ||
return (bbJumpDest != nullptr); | ||
|
@@ -681,7 +716,7 @@ struct BasicBlock : private LIR::Range | |
bool JumpsToNext() const | ||
{ | ||
assert(HasJump()); | ||
return (bbJumpDest == bbNext); | ||
return (bbJumpDest == bbFallThroughSucc); | ||
} | ||
|
||
BBswtDesc* GetJumpSwt() const | ||
|
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.
Here and in a number of other places in this change, the pattern seems to be to change:
BBJ_NONE -> bbNext / BBJ_COND -> bbNext+bbJumpDest
toBBJ_NONE -> GetFallThroughSucc() / BBJ_COND -> GetFallThroughSucc()+bbJumpDest
. TheBBJ_COND
part makes sense, but theBBJ_NONE
changes confuse me.If
GetFallThroughSucc()
is the 'normal' successor ofBBJ_COND
, why touchBBJ_NONE
?Side note: I think you will also want to update
VisitAllSuccs/VisitRegularSuccs
.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'm not sure what @AndyAyersMS's plan is here, but a "lazy" approach to reordering the block layout might be moving a block without considering it might be a
BBJ_NONE
and its successor is its oldbbNext
, and just converting theBBJ_NONE
toBBJ_ALWAYS
after the reorder. If we do something like this, I think it would be nice to always useGetFallThroughSucc
forBBJ_NONE
blocks so we can assert the fall-through successor matchesbbNext
; if we mess something up during block reordering, hopefully this will help us catch it.Thanks for catching that. I'll fix those.
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 feel there is some overloading of meanings for "the fall-through successor" here.
The change at large is needed because in the refactored flowgraph, the "false" successor of
BBJ_COND
(and possibly theBBJ_ALWAYS
part of callfinally pairs) will be distinct frombbNext
. So, overall, the imported blocks will change as follows:BBJ_NONE -> bbNext
->BBJ_ALWAYS -> bbJumpDest
(can be reordered arbitrarily later on).BBJ_COND -> bbNext, bbJumpDest
->BBJ_COND -> bbTheNewFieldBeingAdded, bbJumpDest
(same).In other words, "fall through" will not exist as an explicit flowgraph concept like it does today (perhaps until after a very late layout stage, where
BBJ_NONE
may appear as an optional optimization).In that light, I would expect changes to basically replace
case BBJ_COND: if (<false branch>) return bbNext;
withcase BBJ_COND: if (<false branch>) return bbTheNewFieldBeingAdded;
, and indeed they do in some places like switch recognition, but not in others.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.
My initial plan was to disallow
BBJ_NONE
until we'd set block layout. I think we'll have to chip away at this as we have a lot of code that needs to work both before and after we do layout.So a plausible starting point is to allow
BBJ_NONE
but treat it somewhat likeBBJ_ALWAYS
, meaning it has to explicitly name its flow successor, and find some way to start incrementally removing the earlyBBJ_NONE
cases.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 wonder if simply not producing
BBJ_NONE
(anywhere), instead producingBBJ_ALWAYS
, and adding a small peephole to codegen to not emit "branch to next block" would be a faster/easier way to go about this.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.
With this approach, maybe we can get rid of
bbFallThroughSucc
and instead add a new member specific toBBJ_COND
for its false branch target.