-
Notifications
You must be signed in to change notification settings - Fork 36
Problem of rethrow instruction #30
Description
Problem
The current rethrow
instruction has some problems that make its use very
limited. For example, suppose there are two try/catch blocks and in both catch
they jump to some common code, which is followed by a rethrow
instruction.
block $label0
try
...
catch i
br $label$0
end
...
try
...
catch i
br $label$0
end
end
some common code
rethrow
This cannot be supported in the current spec because rethrow
can occur only in
the scope of a catch
block. But this code pattern is very common especially
when there is some cleanup code (calling destructors) to run before rethrowing
an exception up to a caller. If you compile the code below,
MyClass obj;
try {
foo(); // might throw
} catch (int n) {
...
}
...
try {
foo(); // might throw
} catch (int n) {
...
}
The generated code will look like this:
block $label0
try
call $foo
catch i
br $label$0
end
...
try
...
catch i
br $label$0
end
end
call MyClass's destructor to destruct obj
rethrow
In this case, cleanup action consists of only a single destructor call, but it
can take more code space if there are many objects to destroy, so I think
duplicating this cleanup code into possibly n
catch blocks is not a viable
idea. This is a classic case in which we need a rethrow
, but it cannot be
executed at the end of this code because it's not in the scope of a catch
.
This is one example of code sharing that's very common, but code sharing between
catch blocks can occur in other cases as well. You can use goto
within catch
clauses to jump to a common block. Or any middle-IR-level compiler optimization
pass can factor out some common code in catch blocks.
While this problem can be worked around using not a rethrow
but just a normal
throw
, throw
is considered as throwing a completely new exception, and the
VM wouldn't be able to carry an attached backtrace with it, which can be useful
when we later support backtrace debugging in the future. And more importantly,
this problem effectively makes rethrow
unusable at all, because the most
common usecase of it is, as illustrated above, when it occurs after some common
cleanup code which is shared between many catch blocks. It can also occur when
there is shared cleanup code between catch i
and catch_all
clauses, which
will be very common case as well, but I'm actually planning on proposing
something else for catch
clauses... but anyway.
Idea?
This is a rough idea and not a complete spec yet. And it's not I'm proposing
this as a single concrete alternative and I appreciate comments and suggestions.
I think it is necessary to make it possible to access some kind of handle to an
exception object outside a catch block. (The reason it is not a i32 value but a
handle is it can be opaque if it is for a foreign exception) There can be
multiple ways to do it. We can make catch
instruction to return a handle, save
it to a local or something, and then use it after we exit a catch block. In this
case, rethrow
should take an handle as an argument now.
try
...
catch i
set_local 0, handle
end
get_local 0
use handle
In this case, I think when the VM can destroy an exception object is unclear,
and it can be an issue, maybe? Maybe the VM should maintain a map of handle to
an exception object until the program ends.
Or, to make the VM can destroy exception object when they are not necessary
anymore, we can add some reference count to exception objects, and make a way to
capture an exception handle within a catch block. Suppose capture_exception
instruction captures the current exception handle.
try
...
catch_all
set_local 0, capture_exception
end
get_local 0
use handle
The reference count for an exception starts as 1 when any catch
block is
entered.
- It will be decremented when
- It hits an
try_end
instruction. - When the function execution ends
- It hits an
- It will be incremented when
- When
capture_exception
instruction is executed within acatch
block - When
rethrow
instruction is executed on the handle.
This approach is in a way similar to the newly added library functions to the
C++11 spec:
std::current_exception
, andstd::rethrow_exception
.
- When