Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 8c3b7f1

Browse files
committed
JIT: Remove empty try regions
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.
1 parent 0251195 commit 8c3b7f1

File tree

6 files changed

+403
-58
lines changed

6 files changed

+403
-58
lines changed

Documentation/design-docs/finally-optimizations.md

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ which is almost like a regular function with a prolog and epilog. A
1818
custom calling convention gives the funclet access to the parent stack
1919
frame.
2020

21-
In this proposal we outline two optimizations for finallys: removing
22-
empty finallys and finally cloning.
21+
In this proposal we outline three optimizations for finallys: removing
22+
empty trys, removing empty finallys and finally cloning.
2323

2424
Empty Finally Removal
2525
---------------------
@@ -175,6 +175,42 @@ G_M60484_IG06:
175175
Empty finally removal is unconditionally profitable: it should always
176176
reduce code size and improve code speed.
177177

178+
Empty Try Removal
179+
---------------------
180+
181+
If the try region of a try-finally is empty, and the jitted code will
182+
execute on a runtime that does not protect finally execution from
183+
thread abort, then the try-finally can be replaced with just the
184+
content of the finally.
185+
186+
Empty trys with non-empty finallys often exist in code that must run
187+
under both thread-abort aware and non-thread-abort aware runtimes. In
188+
the former case the placement of cleanup code in the finally ensures
189+
that the cleanup code will execute fully. But if thread abort is not
190+
possible, the extra protection offered by the finally is not needed.
191+
192+
Empty try removal looks for try-finallys where the try region does
193+
nothing except invoke the finally. There are currently two different
194+
EH implementation models, so the try screening has two cases:
195+
196+
* callfinally thunks (x64/arm64): the try must be a single empty
197+
basic block that always jumps to a callfinally that is the first
198+
half of a callfinally/always pair;
199+
* non-callfinally thunks (x86/arm32): the try must be a
200+
callfinally/always pair where the first block is an empty callfinally.
201+
202+
The screening then verifies that the callfinally identified above is
203+
the only callfinally for the try. No other callfinallys are expected
204+
because this try cannot have multiple leaves and its handler cannot be
205+
reached by nested exit paths.
206+
207+
When the empty try is identified, the jit modifies the
208+
callfinally/always pair to branch to the handler, modifies the
209+
handler's return to branch directly to the continuation (the
210+
branch target of the second half of the callfinally/always pair),
211+
updates various status flags on the blocks, and then removes the
212+
try-finally region.
213+
178214
Finally Cloning
179215
---------------
180216

src/jit/compiler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3503,10 +3503,14 @@ class Compiler
35033503

35043504
void fgInline();
35053505

3506+
void fgRemoveEmptyTry();
3507+
35063508
void fgRemoveEmptyFinally();
35073509

35083510
void fgCloneFinally();
35093511

3512+
void fgCleanupContinuation(BasicBlock* continuation);
3513+
35103514
GenTreePtr fgGetCritSectOfStaticMethod();
35113515

35123516
#if !defined(_TARGET_X86_)

src/jit/compphases.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ CompPhaseNameMacro(PHASE_POST_IMPORT, "Post-import",
2626
CompPhaseNameMacro(PHASE_MORPH_INIT, "Morph - Init", "MOR-INIT" ,false, -1)
2727
CompPhaseNameMacro(PHASE_MORPH_INLINE, "Morph - Inlining", "MOR-INL", false, -1)
2828
CompPhaseNameMacro(PHASE_MORPH_IMPBYREF, "Morph - ByRefs", "MOR-BYREF",false, -1)
29+
CompPhaseNameMacro(PHASE_EMPTY_TRY, "Remove empty try", "EMPTYTRY", false, -1)
2930
CompPhaseNameMacro(PHASE_EMPTY_FINALLY, "Remove empty finally", "EMPTYFIN", false, -1)
3031
CompPhaseNameMacro(PHASE_CLONE_FINALLY, "Clone finally", "CLONEFIN", false, -1)
3132
CompPhaseNameMacro(PHASE_STR_ADRLCL, "Morph - Structs/AddrExp", "MOR-STRAL",false, -1)
@@ -103,3 +104,4 @@ CompPhaseNameMacro(PHASE_CLR_API, "CLR API calls",
103104
// clang-format on
104105

105106
#undef CompPhaseNameMacro
107+

0 commit comments

Comments
 (0)