On Dec 1, 2010, at 10:20 AM, Duncan Sands wrote:
> Hi Bill, this proposal seems strangely complicated. I don't see the
advantage
> of the dispatch instruction over just attaching the information to each
invoke.
> Right now you have
>
> invoke void @_Z3foov()
> to label %invcont unwind label %catch.handlers
>
> catch.handlers: landingpad
> dispatch resume to label %...
> catches [
> %struct.__fundamental_type_info_pseudo* @_ZTIi, label %ch.int
> %struct.__pointer_type_info_pseudo* @_ZTIPKc, label %ch.str
> ]
> catchall [i8* null, label % ch.ca]
>
> Why not combine this into:
>
> invoke void @_Z3foov()
> to label %invcont unwind resume to label %...
> catches [
> %struct.__fundamental_type_info_pseudo* @_ZTIi, label %ch.int
> %struct.__pointer_type_info_pseudo* @_ZTIPKc, label %ch.str
> ]
> catchall [i8* null, label % ch.ca]
>
> ? That would get rid of the "landingpad" basic block attribute,
and rules about
> what can branch to landing pads, that these cannot be split etc. Your
rules
> mean that you can uniquely associate a dispatch instruction with each
invoke
> unwind edge, so why not directly attach the dispatch information to the
edge,
> i.e. to the invoke?
>
That was my first proposal (way back when). I ran into the problem of cleanups.
Basically, they get in the way of the decision making process. Consider this:
invoke void @_Z3foov()
to label %invcont unwind resume to label %...
catches [
%struct.__fundamental_type_info_pseudo* @_ZTIi, label %ch.int
%struct.__pointer_type_info_pseudo* @_ZTIPKc, label %ch.str
]
catchall [i8* null, label % ch.ca]
Now consider that a cleanup must occur here. How do we represent this here and
still keep the information that "int" goes to ch.int and "const
char *" goes to ch.str? The cleanups must happen before we get to ch.int,
ch.str, and ch.ca. And cleanup code can be arbitrarily complex – including the
fact that it may not return, or it may have multiple exit points. You end up
needing a dispatch-like instruction at the end of the cleanup region so that you
can associated the invoke to the decision point – the point where we determine
which handler we need to execute. After we realized this, this proposal became
the natural way.
>> Syntax:
>>
>> dispatch resume to label<resumedest>
>
> How do you say: if nothing is matched, keep unwinding out of the function?
> Actually I don't see why <resumedest> is useful at all, since you
can always
> place a complete set of all handlers into one dispatch, so redispatching is
> not needed. That said, it might be handy for reducing code duplication.
>
The <resumedest> will have code to handling unwinding out of the function.
Normally, it will just call _Unwind_Resume(). As you saw in the example, though,
it may call other things (like `terminate()'). I would love to have a
language-neutral way of saying "resume unwinding". I don't know if
it's sufficient to rely upon every implementation to call _Unwind_Resume().
>> catches [
>> <type> <val>, label<dest1>
>> ...
>> ]
>> catchall [<type> <val>, label<dest> ]
>> personality [<type> <value>]
>> filters [
>> <type> <val>,<type> <val>, ...
>
> You also need labels for filters. I know it may not look like it, but
> if you inline a function with a filter into a function with handlers
> then the filter ends up being nested inside outer handlers, resulting
> in the need for a destination. In fact inlining also means that you
> can't just list filters after catches, you need to consider maybe
> having some catches, then a filter, then some other catches etc. Or
> perhaps you want to take care of it by chaining dispatches via the
> resumedest?
>
When I looked at code which inlined functions with filters, it seemed like they
were attached to the throwing instructions. In that case, you will have the
dispatch of the inlined function, which will be associated with its invokes, and
the filter information will exist there. In that way, we have the
"nesting" that you described.
>> ]
>>
>> The `catches', `catchall', and `filters' clauses are
optional. If neither `catches' nor `catchall' is specified, then the
landing pad is implicitly a cleanup.
>
> You may have cleanups to run even if there are handlers, how do you
distinguish
> between "have handlers, plus a cleanup to run in any case" and
"have handlers,
> no cleanup needs to be run if handlers don't match, just unwind
straight
> through"?
>
The first case is my example. E.g.:
lpad: landingpad
call void @cleanup(%A* %a)
dispatch resume to label %unwind
catches [ . . . ]
etc.
The second one would be the job of the front-end to generate the correct
dispatch. E.g:
lpad: landingpad
dispatch resume to label %unwind
catches [ . . . ]
>> • The `filters' clause lists the types of exceptions which may be
thrown by the
>> region.
>
> What is "a region"?
>
I defined it in the first document, which was referenced:
A "region" is defined as a section of code where, if an exception is
thrown
anywhere within that section, control is passed to code that will handle the
exception (called a "landing pad"). The code which handles the
specific
exception is unique to that region.
>> onto.catch.handlers:
>> dispatch resume to %lpad
>
> What is %lpad?
>
A typo. It should be %catch.handlers
> As a general remark, I don't see how this is superior to gcc's
region scheme,
> which could be adapted to LLVM by (in essence) attaching to an invoke the
list
> of all regions (and their info like list of catches) that contain the
invoke.
> If you do that then you get something rather close to your proposal, only
the
> dispatch instructions have become part of the invokes, you don't need
the funny
> landing pad special semantics etc.
>
Could you describe GCC's region scheme?
-bw
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://lists.llvm.org/pipermail/llvm-dev/attachments/20101201/cd9b5b03/attachment.html>