Hi John,
>> Inlining
>> --------
>>
>> Many a plausible seeming exception handling scheme has fallen by the
way-side
>> because it interacts poorly with inlining.
>>
>> Here is how inlining would work with this scheme. It's pretty
close to how
>> it works right now. Suppose you have
>>
>> invoke void @foo()
>> to label %invcont unwind label %lpad<foo catch info>
>>
>> and you want to inline foo. Suppose foo contains an invoke:
>>
>> invoke void @bar()
>> to label %invcont2 unwind label %lpad2<bar catch info>
>>
>> Then after inlining you have an invoke of bar in which foo's catch
info
>> has been appended to bar's:
>>
>> invoke void @bar()
>> to label %invcont2 unwind label %lpad2<joined catch
info>
>>
>> What does appending<foo catch info> to<bar catch info>
mean? If the
>> personality functions are different then you are in trouble and need to
>> disallow the inlining! The cleanup flag is the "or" of the
foo and bar
>> cleanup flags. The catches are the bar catches followed by the foo
>> catches.
>
> You are greatly underestimating the amount of work the inliner has to do
under this proposal. In fact, the only thing that your proposal simplifies is
DwarfEHPrepare.
needless to say, I disagree :) See below.
> The inliner would still need to do two extra things:
>
> 1. It would need to adjust landing pads so that they forward selectors
they don't understand to the enclosing landing pad. Consider the following
code:
>
> void a();
> void b() {
> try { a(); } catch (int i) {}
> }
> void c() {
> try { b(); } catch (float f) {}
> }
>
> The landing pad in b() only has one case to worry about, so it's
naturally going to immediately enter the catch handler for 'int'. This
is obviously semantically wrong if the combined invoke unwinds there.
Here is the LLVM IR for functions "b" and "c".
define void @_Z1bv() {
entry:
invoke void @_Z1av()
to label %return unwind label %"<L1>" personality
@__gxx_personality_v0 catches %struct.__fundamental_type_info_pseudo* @_ZTIi
"<L1>": ; preds =
%entry
%exc_ptr = tail call i8* @llvm.eh.exception()
%filter = tail call i32 @llvm.eh.selector()
%typeid = tail call i32 @llvm.eh.typeid.for(i8* bitcast
(%struct.__fundamental_type_info_pseudo* @_ZTIi to i8*))
%0 = icmp eq i32 %filter, %typeid
br i1 %0, label %"<L2>", label %"<bb 5>"
"<bb 5>": ; preds =
%"<L1>"
unwind
"<L2>": ; preds =
%"<L1>"
%D.2112_2 = tail call i8* @__cxa_begin_catch(i8* %exc_ptr) nounwind
tail call void @__cxa_end_catch() nounwind
ret void
return: ; preds = %entry
ret void
}
define void @_Z1cv() {
entry:
invoke void @_Z1bv()
to label %return unwind label %"<L1>" personality
@__gxx_personality_v0 catches %struct.__fundamental_type_info_pseudo* @_ZTIf
"<L1>": ; preds =
%entry
%exc_ptr = tail call i8* @llvm.eh.exception()
%filter = tail call i32 @llvm.eh.selector()
%typeid = tail call i32 @llvm.eh.typeid.for(i8* bitcast
(%struct.__fundamental_type_info_pseudo* @_ZTIf to i8*))
%0 = icmp eq i32 %filter, %typeid
br i1 %0, label %"<L2>", label %"<bb 5>"
"<bb 5>": ; preds =
%"<L1>"
unwind
"<L2>": ; preds =
%"<L1>"
%D.2106_2 = tail call i8* @__cxa_begin_catch(i8* %exc_ptr) nounwind
tail call void @__cxa_end_catch() nounwind
ret void
return: ; preds = %entry
ret void
}
Here is the LLVM IR when you inline "b" into "c" according
to the rules I
stated:
define void @_Z1cv() {
entry:
invoke void @_Z1av()
to label %return.i unwind label %"<L1>.i" personality
@__gxx_personality_v0 catches %struct.__fundamental_type_info_pseudo* @_ZTIi,
%struct.__fundamental_type_info_pseudo* @_ZTIf
"<L1>.i": ; preds =
%entry
%exc_ptr.i = call i8* @llvm.eh.exception()
%filter.i = call i32 @llvm.eh.selector()
%typeid.i = call i32 @llvm.eh.typeid.for(i8* bitcast
(%struct.__fundamental_type_info_pseudo* @_ZTIi to i8*))
%0 = icmp eq i32 %filter.i, %typeid.i
br i1 %0, label %"<L2>.i", label %"<bb 5>.i"
"<bb 5>.i": ; preds =
%"<L1>.i"
br label %"<L1>"
"<L2>.i": ; preds =
%"<L1>.i"
%D.2112_2.i = call i8* @__cxa_begin_catch(i8* %exc_ptr.i) nounwind
call void @__cxa_end_catch() nounwind
br label %_Z1bv.exit
return.i: ; preds = %entry
br label %_Z1bv.exit
_Z1bv.exit: ; preds = %return.i,
%"<L2>.i"
br label %return
"<L1>": ; preds =
%"<bb 5>.i"
%exc_ptr = tail call i8* @llvm.eh.exception()
%filter = tail call i32 @llvm.eh.selector()
%typeid = tail call i32 @llvm.eh.typeid.for(i8* bitcast
(%struct.__fundamental_type_info_pseudo* @_ZTIf to i8*))
%1 = icmp eq i32 %filter, %typeid
br i1 %1, label %"<L2>", label %"<bb 5>"
"<bb 5>": ; preds =
%"<L1>"
unwind
"<L2>": ; preds =
%"<L1>"
%D.2106_2 = tail call i8* @__cxa_begin_catch(i8* %exc_ptr) nounwind
tail call void @__cxa_end_catch() nounwind
ret void
return: ; preds = %_Z1bv.exit
ret void
}
Looks good, right? It is true that I forgot to say that front-ends have to
output code to continue unwinding an exception if they don't recognise the
selector. I amended my proposal to say that it is unspecified whether a
non-matching exception results in a branch to the unwind label or not - this
forces front-ends to rewind any exceptions that don't match any of their
catches.
>
> 2. It would need to rewrite calls to _Unwind_Resume on cleanup-only paths
if the enclosing invoke has a handler. The EH machinery does not expect a
landing pad which claims to handle an exception to just call _Unwind_Resume
before handling it; that's why we currently have to use hacks to call
_Unwind_Resume_or_Rethrow instead.
Nope, because when inlining through an invoke the inliner appends the
invoke's
catch info to everything it inlines. This is the key point that makes the
cleanup problem go away. As an optimization it is possible to convert an invoke
of _Unwind_Resume into a branch, but I think it would be better for front-ends
to output an "unwind" instruction instead of _Unwind_Resume and do
this
optimization on "unwind".
> Also, some platforms provide an alternative to _Unwind_Resume that
doesn't require the compiler to pass the exception pointer; we'd like
to be able to use those.
The code generators can lower the "unwind" instruction to whatever the
platform
prefers to use to continue unwinding the exception. For example on ARM with
EABI you wouldn't want unwind to be lowered to _Unwind_Resume.
>
> The 'dispatch' instruction simplifies this by presenting an obvious
place to rewrite (which doesn't actually require rewriting — you just
add/redirect the 'resume' edge to point to the enclosing landing pad).
As explained above no rewriting is needed with this scheme.
Best wishes,
Duncan.