-
Notifications
You must be signed in to change notification settings - Fork 2.6k
JIT: Remove empty try regions #8949
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 |
---|---|---|
|
@@ -18,8 +18,8 @@ which is almost like a regular function with a prolog and epilog. A | |
custom calling convention gives the funclet access to the parent stack | ||
frame. | ||
|
||
In this proposal we outline two optimizations for finallys: removing | ||
empty finallys and finally cloning. | ||
In this proposal we outline three optimizations for finallys: removing | ||
empty trys, removing empty finallys and finally cloning. | ||
|
||
Empty Finally Removal | ||
--------------------- | ||
|
@@ -175,6 +175,42 @@ G_M60484_IG06: | |
Empty finally removal is unconditionally profitable: it should always | ||
reduce code size and improve code speed. | ||
|
||
Empty Try Removal | ||
--------------------- | ||
|
||
If the try region of a try-finally is empty, and the jitted code will | ||
execute on a runtime that does not protect finally execution from | ||
thread abort, then the try-finally can be replaced with just the | ||
content of the finally. | ||
|
||
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 the finally ensures | ||
that the cleanup code will execute fully. But if thread abort is not | ||
possible, the extra protection offered by the finally is not needed. | ||
|
||
Empty try removal looks for try-finallys where the try region does | ||
nothing except invoke the finally. There are currently two different | ||
EH implementation models, so the try screening has two cases: | ||
|
||
* callfinally thunks (x64/arm64): the try must be a single empty | ||
basic block that always jumps to a callfinally that is the first | ||
half of a callfinally/always pair; | ||
* non-callfinally thunks (x86/arm32): the try must be a | ||
callfinally/always pair where the first block is an empty callfinally. | ||
|
||
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 multiple leaves and its handler cannot be | ||
reached by nested exit paths. | ||
|
||
When the empty try is identified, the jit modifies the | ||
callfinally/always pair to branch to the handler, modifies the | ||
handler's return to branch directly to the continuation (the | ||
branch target of the second half of the callfinally/always 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 commentThe reason will be displayed to describe this comment to others. Learn more. In the "callfinally thunks" case, you'll have 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, 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... |
||
Finally Cloning | ||
--------------- | ||
|
||
|
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.