On Nov 24, 2010, at 9:01 PM, Bill Wendling wrote:> On Nov 24, 2010, at 6:41 PM, John McCall wrote: >> What you mean is that, given a resume or invoke edge, we need to be able to find the dispatch for the target region. There are ways to make that happen without tagged edges; for example, you could make the landing pad a special subclass of BasicBlock with a pointer to the dispatch, although that'd be a fairly invasive change. Tagging the edges solves the problem for clients with a handle on an edge; clients that want to go from (say) a dispatch to its landing pad(s) will still have trouble. > > It's not that troublesome. The dispatch would give you the region number. All objects in the function with that region number will point to the landing pad(s) for that region.Well, so a linear scan of the function seems like trouble to me, but I shouldn't worry too much about optimizing a theoretical client. :) Anyway, I'd like to table this part of the discussion while we resolve the other half. We're arguing about two things: 1. Whether edges leading to landing pads should also be tagged with the region. This is, well, more important than a bike shed, but still insignificant relative to: 2. Whether a single region can have multiple landing pads. This is actually a pretty central question in the design. Plus the answer to #2 may obviate the need for discussion on #1 anyway.>>> We know from experience that once this information is lost, it's *really* hard to get it back again. That's what DwarfEHPrepare.cpp is all about, and I want to get rid of that pass because it's a series of hacks to get around our poor EH support. >> >> While I agree that the hacks need to go, there is always going to be some amount of custom codegen for EH just to get the special data to flow properly from landing pads to the eh.exception intrinsic and the dispatch instruction. My design would give you some very powerful assumptions to work with to implement that: both the intrinsic calls and the dispatch would always be dominated by the region's landing pad, which would in turn only be reachable via specific edges. I don't know how you're planning on implementing this without those assumptions, but if you say you don't need them, that certainly diminishes the appeal of my proposal. > > We actually have the "reachable via specific edges" check in our code right now. But when we tried to allow exceptions to be marked as proper "cleanups", the assumption was violated. So I'm wary of making this same assumption twice. > > But anyway, I think that I can gather the necessary information from the region numbers and the invokes' "unwind to" edges to create the EH tables. The only intrinsic call that should remain is the one that gets the EH pointer. And that's only needed by the catch blocks.Right, I agree that it's easy to assemble the EH tables for a given invoke under any of these variants. We don't need any new constraints to make this easier. What I'm talking about is the flow of data from the start of the landing pad to two points: 1. The (optional) call to @llvm.eh.exception. 2. The dispatch instruction. Specifically, in DWARF EH, the personality function writes these two values into the frame somewhere — maybe into registers, maybe into the EH buffer, whatever — and that information needs to flow to the appropriate intrinsic/instruction. You can't just stash it aside, because the cleanup might throw and catch an exception between points A and B. I'm really not sure how this is supposed to work if there's no guaranteed relationship between the landing pad and the intrinsic/dispatch (†). The most sensible constraint — dominance — is equivalent to saying that each region has at most one landing pad. (†) Technically, there can be no true guarantee because the dispatch doesn't even need to be reachable from its landing pad. For example: extern void foo(); struct A { ~A() { throw 0; } }; void test() { A a; foo(); } After inlining, the cleanup landing pad in test() will contain a throw to a terminating handler, and therefore the dispatch will not be reachable. So any constraint has to be something like "either the dispatch is unreachable or it's dominated by the entry to the landing pad". But it's actually quite easy to write correct code to handle this case. :) John.
On Thu, Nov 25, 2010 at 8:49 AM, John McCall <rjmccall at apple.com> wrote:> (†) Technically, there can be no true guarantee because the dispatch doesn't even need to be reachable from its landing pad. For example: > extern void foo(); > struct A { ~A() { throw 0; } }; > void test() { A a; foo(); } > After inlining, the cleanup landing pad in test() will contain a throw to a terminating handler, and therefore the dispatch will not be reachable. So any constraint has to be something like "either the dispatch is unreachable or it's dominated by the entry to the landing pad". But it's actually quite easy to write correct code to handle this case. :)Doesn't "the dispatch is unreachable" already imply that it's dominated by the landing pad? All paths to the dispatch (i.e. none at all) will first go through the landing pad. So that constraint is just "the dispatch is dominated by the entry to the landing pad".
On Nov 24, 2010, at 11:49 PM, John McCall wrote:> On Nov 24, 2010, at 9:01 PM, Bill Wendling wrote: >> On Nov 24, 2010, at 6:41 PM, John McCall wrote: >>>> We know from experience that once this information is lost, it's *really* hard to get it back again. That's what DwarfEHPrepare.cpp is all about, and I want to get rid of that pass because it's a series of hacks to get around our poor EH support. >>> >>> While I agree that the hacks need to go, there is always going to be some amount of custom codegen for EH just to get the special data to flow properly from landing pads to the eh.exception intrinsic and the dispatch instruction. My design would give you some very powerful assumptions to work with to implement that: both the intrinsic calls and the dispatch would always be dominated by the region's landing pad, which would in turn only be reachable via specific edges. I don't know how you're planning on implementing this without those assumptions, but if you say you don't need them, that certainly diminishes the appeal of my proposal. >> >> We actually have the "reachable via specific edges" check in our code right now. But when we tried to allow exceptions to be marked as proper "cleanups", the assumption was violated. So I'm wary of making this same assumption twice. >> >> But anyway, I think that I can gather the necessary information from the region numbers and the invokes' "unwind to" edges to create the EH tables. The only intrinsic call that should remain is the one that gets the EH pointer. And that's only needed by the catch blocks. > > Right, I agree that it's easy to assemble the EH tables for a given invoke under any of these variants. We don't need any new constraints to make this easier. > > What I'm talking about is the flow of data from the start of the landing pad to two points: > 1. The (optional) call to @llvm.eh.exception. > 2. The dispatch instruction. > Specifically, in DWARF EH, the personality function writes these two values into the frame somewhere — maybe into registers, maybe into the EH buffer, whatever — and that information needs to flow to the appropriate intrinsic/instruction. You can't just stash it aside, because the cleanup might throw and catch an exception between points A and B. I'm really not sure how this is supposed to work if there's no guaranteed relationship between the landing pad and the intrinsic/dispatch (†). The most sensible constraint — dominance — is equivalent to saying that each region has at most one landing pad.I did a sample test with gcc and it is stashing the information off to the side. #include <cstdio> void foo(); void qux(); struct A { ~A() { try { throw 0; } catch (double) { printf("hoo\n"); } } }; void bar() { try { A a; foo(); } catch (const char *c) { printf("hello world! %s\n", c); } } The cleanup part for the "foo" call has this: L24: # basic block 4 L3: movl %edx, %ebx movq %rax, %r13 movl $4, %edi call ___cxa_allocate_exception movq %rax, %rdi movl $0, (%rax) movq __ZTIi at GOTPCREL(%rip), %rsi xorl %edx, %edx LEHB2: call ___cxa_throw LEHE2: edx and rax hold the EH selector value and EH object pointer respectively. They get restored before continuing on to the catch in bar (if they get there, which I don't think they will in this case). But that's beside the point. I see what you mean. There is an impedance mismatch going on here. :-) Your use of dominance as a constraint was confusing me. I characterize it as losing information, but it results in the same problem. I.e., after a throw and catch in a cleanup, we need to be able to reconstruct the original EH value and object pointer, but cannot determine that if we are simply branching to yet another block from that catch. Using the dispatch instruction gives us this information. The proposal can be easily modified to the "landing pad dominates the dispatch" view of the world. And if we're careful, we can keep the guarantee that only unwind and dispatch edges may jump to a landing pad throughout the optimizer. :-) Thanks for shedding light on this! -bw
On 25 November 2010 10:41, Bill Wendling <wendling at apple.com> wrote:> The proposal can be easily modified to the "landing pad dominates the dispatch" view of the world. And if we're careful, we can keep the guarantee that only unwind and dispatch edges may jump to a landing pad throughout the optimizer. :-)I agree this is a reasonable and necessary assumption to make it safe. ;) cheers, --renato