-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Conversation
Will hold off on merge pending @kouvel's fix noted in #8924. @Brucefo PTAL; cc @dotnet/jit-contrib Impacts 130 methods in the jit-diff assembly set:
|
You probably wanted @BruceForstall |
Yeah, thanks. |
because this try cannot have multple leaves and its handler cannot be | ||
reached by nested exit paths. | ||
|
||
When the empty try is identified, the jit modifies the callaways pair |
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.
callaways -> callalways?
Odd-looking ubuntu failure:
Hopefully not an indicator that ongoing CoreFX changes are leaking through.... Will retry. @dotnet-bot retest Ubuntu x64 Checked Build and Test |
Retesting ubuntu now that #8938 is in... @dotnet-bot retest Ubuntu x64 Checked Build and Test |
OSX seems similarly afflicted; will let it fail before I retry. |
@dotnet-bot retest OSX x64 Checked Build and Test |
|
||
Empty trys with non-empty finallys often exist in code that must run | ||
under both thread-abort aware and non-thread-abort aware runtimes. In | ||
the former case the placement of cleanup code in thea finally ensures |
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.
thea [](start = 49, length = 4)
typo: "thea"
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.
Fixed
|
||
* callfinally thunks (all but x86): the try must be a single empty | ||
basic block that always jumps to a callfinally that is the first | ||
half of a callalways pair; |
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.
callalways [](start = 10, length = 10)
can you write out "callallways pair" as "callfinally/always pair"? it's easier to see it's actually a "pair" in that case, and it matches the JIT BBJ_* names more closely.
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.
Yep, will fix.
branch directly to the continuation (the branch target of the second | ||
half of the callalways pair), updates various status flags on the | ||
blocks, and then removes the try-finally region. | ||
|
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.
In the "callfinally thunks" case, you'll have always->callfinally
, converted to always->always
. Can you just get rid of the thunk entirely and redirect this thunk always?
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.
Yes, you certainly could do that.
I've been on the fence with these optimizations deciding how much cleanup they should do. On the one hand it seems entirely reasonable to let the subsequent flow opts clean things up, and leaving redundant and/or unreachable blocks around doesn't seem to cause trouble or inhibit other opts. On the other it does not take to much extra work to fix things here.
In the callfinally thunk case the only reference to the callfinally will be in the try, and we already know that the try is a single empty jump-always block. So it would be easy enough to retarget that instead.
That still leaves dead blocks around, and flow opts will almost certainly remove the try block itself later on...
EH implementation models, so the try screening has two cases: | ||
|
||
* callfinally thunks (all but x86): the try must be a single empty | ||
basic block that always jumps to a callfinally that is the first |
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.
ARM32 doesn't use thunks.
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.
Will update to note x64/arm64 use callfinally thunks, x86/arm32 do not.
@@ -100,6 +100,7 @@ CompPhaseNameMacro(PHASE_CLR_API, "CLR API calls", | |||
#endif | |||
|
|||
CompPhaseNameMacro(PHASE_EMPTY_FINALLY, "Remove empty finally", "EMPTYFIN", false, -1) | |||
CompPhaseNameMacro(PHASE_EMPTY_TRY, "Remove empty try", "EMPTYTRY", false, -1) | |||
CompPhaseNameMacro(PHASE_CLONE_FINALLY, "Clone finally", "CLONEFIN", false, -1) |
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.
nit: since you made empty try removal execute first, maybe you can list it first here, too. (and in the function declarations)
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.
Sure
// Code in a finally gets special treatment in the presence of | ||
// thread abort. | ||
bool enableRemoveEmptyTry = false; | ||
#endif // FEATURE_CORECLR |
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.
Maybe you could add a debug way to force enable this using a COMPLUS variable, even on desktop?
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.
Sure, will add the same sort of override as we have for finally cloning.
if (verbose) | ||
{ | ||
printf("\n*************** After fgRemoveEmptyFinally()\n"); | ||
fgDispBasicBlocks(); |
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.
copy-paste error: rename func
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.
Fixed.
|
||
// (6) Remove the try-finally EH region. This will compact the | ||
// EH table so XTnum now points at the next entry and will update | ||
// the EH regon indices of any nested EH in the (former) handler. |
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.
regon [](start = 18, length = 5)
typo: regon
if (!HBtab->HasFinallyHandler()) | ||
{ | ||
JITDUMP("EH#%u is not a try-finally; skipping.\n", XTnum); | ||
XTnum++; |
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.
Seems like this output is going to be a bit verbose in the normal case. Well, not like LSRA verbose...
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.
There aren't that many EH regions... but I can tone it down if you prefer.
assert(verifiedSingleCallfinally); | ||
continue; | ||
} | ||
|
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.
Should this whole section be under #ifdef DEBUG
? Or are you worried there might actually be such cases that you haven't considered, so this is just defense-in-depth, and you'll wait to see if any asserts come in?
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.
It's not easy for me to test all the EH model variants locally, so this was just extra paranoia. Will probably leave it since it is pretty cheap.
@AndyAyersMS Mostly minor comments, overall looks very good. |
00e325a
to
75f75d7
Compare
@BruceForstall think I got to most of your feedback with the updates. Rebased to pick up the fix in #8952 and pulled that bit out as a helper method since it is now needed by 3 finally opts. |
|
||
The screening then verifies that the callfinally identified above is | ||
the only callfinally for the try. No other callfinallys are expected | ||
because this try cannot have multple leaves and its handler cannot be |
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.
multple [](start = 29, length = 7)
typo: multple
reached by nested exit paths. | ||
|
||
When the empty try is identified, the jit modifies the | ||
callfinally/aways pair to branch to the handler, modifies the |
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.
aways [](start = 12, length = 5)
typo: aways
|
||
When the empty try is identified, the jit modifies the | ||
callfinally/aways pair to branch to the handler, modifies the | ||
handler's return points to branch directly to the continuation (the |
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.
Is the word "points" unnecessary? Seems confusing.
Will need to be reconciled with #9065. |
This PR is creating a big buzz at the office. We're all just now learning about the concept of .NET Core and runtimes and so on, so we're extremely ignorant. Could someone explain the circumstances under which 'FEATURE_CORECLR' (referenced in src/jit/flowgraph.cpp) is false, and why code in the CoreCLR repo needs to worry about the 'FEATURE_CORECLR' being false? It really confused us when we saw that line because it seems contradictory. And also when we saw 'If the JIT is used on runtimes that do support thread abort, I guess it shouldn't do it there...'. So there are more runtimes at play than just CoreCLR? Can someone explain what they are? Thanks 😄 |
@davghouse There's CoreCLR (aka .NET Core) and there's .NET Framework, the Windows only version of .NET from which .NET Core was created. The runtime code is more or less shared between .NET Core and .NET Framework so in some cases care needs to be taken to avoid breaking .NET Framework. |
@davghouse We try to ensure that JIT changes in particular can flow back to the JIT used in desktop CLR, where thread abort is supported. |
a3bd368
to
8c3b7f1
Compare
Rebased and updated past merge conflict in compphases.h, and squashed to one commit. |
Looks like test runner script errors on OSX & Ubuntu. Maybe #9092 has fixed this, will retry.
@dotnet-bot retest OSX x64 Checked Build and Test |
OSX and Ubuntu still broken, seems to be hitting all recent runs. The runtime can't initialize itself:
|
Retrying these now that recent CI issues were cleared up. @dotnet-bot retest OSX x64 Checked Build and Test |
In runtimes that do not support thread abort, a try-finally with an empty try can be optimized to simply the content of the finally. See documentation update for more details. Closes #8924.
7444b39
to
f2fda54
Compare
Rebasing to try get past the latest CI issues. |
One more try with the last round of CI updates. @dotnet-bot retest OSX x64 Checked Build and Test |
OSX failed in an unusual way (log), will retry.
@dotnet-bot retest OSX x64 Checked Build and Test |
@BruceForstall I think this is ready.... |
// In runtimes where thread abort is not possible, `try {} finally {S}` | ||
// can be optimized to simply `S`. This method looks for such | ||
// cases and removes the try-finally from the EH table, making | ||
// suitable flow, block flag, statement, and reigon updates. |
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.
reigon [](start = 48, length = 6)
typo: reigon
JITDUMP("\n*************** In fgRemoveEmptyTry()\n"); | ||
|
||
#if FEATURE_CORECLR | ||
bool enableRemoveEmptyTry = true; |
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 needs to be #ifdef
. Same for the case in fgCloneFinally().
XTnum++; | ||
continue; | ||
} | ||
|
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.
It's interesting that all 3 of your try/finally optimizations loop the EH table looking for finally. Not a big deal. And keeps the optimization functions self-contained and independent. But you could presumably set a global "this method has a finally" bool set during EH import. (Assuming nobody adds finallys afterwards, which I think is true (although fgAddSyncMethodEnterExit() for synchronized methods on non-x86 injects a try/fault)).
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.
We're downstream of fgAddInternal
so we see the injected EH. But since it's a manually-cloned finally we don't do anything with it.
Will leave the structure as is for now...
Found a couple nits. Otherwise, LGTM. You should turn it on for desktop (using your complus var) and run desktop SPMI asm diffs to check diffs and ensure no amd64 asserts across more code. |
No asserts in desktop SPMI. Lots of diffs. Around 50K methods with size improvements (often substantial) and 10 or so with small regressions. |
Number of empty trys is much less than 50K though, there is a bit of version skew with the baseline and so I see frequent small unrelated diffs. |
In runtimes that do not support thread abort, a try-finally with an empty try can be optimized to simply the content of the finally. See documentation update for more details. Closes dotnet/coreclr#8924. Commit migrated from dotnet/coreclr@119b04c
In runtimes that do not support thread abort, a try-finally with
an empty try can be optimized to simply the content of the finally.
See documentation update for more details.
Closes #8924.