Closed zerbina closed 1 week ago
A MIR Finally
is only usable for intercepting exceptional control-flow now, and thus it cannot be used for destructor calls anymore. For implementing scope cleanup, one possible solution would have been to keep the merged destructor blocks and use dispatchers (which is what ccgflow
previously did, in the end), but I opted for inlining the destructors directly at the gotos instead.
To visualize what I mean, consider the following:
block L1:
var x = ...
if ...: break L1
var y = ...
if ...: break L1
Previously, this resulted in MIR code that is equivalent to:
block L1:
var x = ...
try:
if ...: break L1
var y = ...
try:
if ...: break L1
finally:
`=destroy`(y)
finally:
`=destroy`(x)
Now, the generated MIR code is equivalent to:
block L1:
var x = ...
if ...:
`=destroy`(x)
break L1
var y = ...
if ...:
`=destroy`(y)
`=destroy`(x)
break L1
`=destroy`(y)
`=destroy`(x)
While both are functionally equivalent, the inlined version allows for more static elision of =destroy
calls and destructive moves (wasMoved
), thus possibly reducing run-time overhead.
I made a test with the compiler code-base in order to get some concrete performance numbers. Let A
be the compiler from fb4665a518b68c1d329208950520e4ff648b1649 (devel
, at the time of writing). Let B
be the compiler from this PR.
Using a -d:release
version of A
that was built by A
, the time it takes to compile A
is:
Time (mean ± σ): 14.955 s ± 0.156 s
Using a -d:release
version of A
that was built by B
, the time it takes to compile A
is:
Time (mean ± σ): 14.641 s ± 0.047 s
/merge
Merge requested by: @zerbina
Contents after the first section break of the PR description has been removed and preserved below:
## Note For Reviewers * reviewing the commits individually is likely going to be easier than reviewing everything together
Summary
finally
into ablock
+case
dispatcher inmirgen
mirgen
alreadybreak
andreturn
Details
The goals are to:
For the MIR:
Finally
can only be used as the target for exceptional jumps; unstructured control-flow out of aFinally
section is disallowedContinue
Raise
is decoupled from exception management; it only initiates unwinding nowThe various MIR processing, rendering, and translation is adjusted accordingly. The CGIR equivalents to the MIR constructs change in the same way.
Since
Finally
can no longer be used with non-exceptional control-flow (Goto
,Case
, etc.), translation of both scope cleanup andfinally
needs to change inmirgen
. The exception runtime calls for managing the exception stack also have to be injected inmirgen
already.finally
Loweringfinally
is translated into continuation passing. However, instead of reifying thefinally
into a standalone procedure, all "invocations" of (read, jumps to) thefinally
record their original destination in a local variable, which a dispatcher emitted at the end of thefinally
clause then uses to jump to the target (or another interceptingfinally
).Scope Cleanup
Scope cleanup in response to an exception being raised still uses
Finally
. For cleanup in response tobreak
orreturn
, all necessary cleanup is emitted directly before theGoto
. This increases the workload for the move analysis / destructor elision pass, but it also results in more efficient code (since there are usually less dynamic invocations of destructors).Code Generation
All three code generators are updated to consider the new syntax and semantics of
Finally
,Continue
, etc. Notably:ccgflow
is obsolete and thus removed; code generation forFinally
,Continue
, etc. is now simple enough to implement directly incgen
jsgen
now translatesFinally
to a JavaScriptcatch
clause (so as to not interfere withbreak
s), which simplifies code generation (no more enabling/disabling offinally
clauses for breaks) and also improves code size / performance in some casesException Runtime
nimAbortException
has aviaRaise
parameter now, as the C-specific error flag cannot be used anymore for detecting what causes the abortprepareException
is merged back intoraiseExceptionAux
The JavaScript target also using the C exception runtime now fixes a bug where breaking out of a
finally
didn't clean up the current exception properly.VM
Exception stack management is partially decoupled from the core VM and moved to
vmops
(which hooks and implements the various exception runtime procedures). Generating the stacktrace still needs to be done directly by the VM, which prevents the exception stack management from being fully decoupled.