<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><div><div>On Dec 2, 2010, at 2:21 AM, Duncan Sands wrote:</div><br class="Apple-interchange-newline"><blockquote type="cite"><div>Hi Bill,<br><br><blockquote type="cite">This is similar to my first proposal.<br></blockquote><br>yup, I still consider your first proposal to have been basically sound.<br><br>But it also suffers from a major problem,<br><blockquote type="cite">which stopped that proposal dead in its tracks. Namely, you have information in<br></blockquote><blockquote type="cite">one place which needs to be shared in two different, but possibly disjoint,<br></blockquote><blockquote type="cite">places: the type, filters, and personality information. In order to generate the<br></blockquote><blockquote type="cite">EH tables, you need to know this information at the throw site and at the place<br></blockquote><blockquote type="cite">which makes the decision of which catch handler to invoke. There is no guarantee<br></blockquote><blockquote type="cite">in your proposal that the invoke can be associated with the proper eh.selector<br></blockquote><blockquote type="cite">call. And because of (C++) cleanups and inlining, it's the rule not the exception.<br></blockquote><br>I disagree that this information is needed anywhere except the invoke. If it<br>was needed arbitrarily far downstream then of course my proposal would be dead.<br>But it isn't! Got an example where it is?<br><br><blockquote type="cite">Example, if you have this:<br></blockquote><blockquote type="cite"><br></blockquote><blockquote type="cite">invoke void @foo()<br></blockquote><blockquote type="cite">to label %invcont unwind label %lpad<br></blockquote><blockquote type="cite">personality @__gxx_personality_v0<br></blockquote><blockquote type="cite">catches %struct.__fundamental_type_info_pseudo* @_ZTIi,<br></blockquote><blockquote type="cite">%struct.__pointer_type_info_pseudo* @_ZTIPKc<br></blockquote><blockquote type="cite"><br></blockquote><blockquote type="cite">lpad:<br></blockquote><blockquote type="cite">call void @bar(%A* %a) ; a cleanup<br></blockquote><blockquote type="cite">br label %ppad<br></blockquote><blockquote type="cite"><br></blockquote><blockquote type="cite">ppad:<br></blockquote><blockquote type="cite">%eh_ptr = call i8* llvm.eh.exception()<br></blockquote><blockquote type="cite">%eh_sel = call i32 llvm.eh.selector()<br></blockquote><blockquote type="cite">; code to clean up.<br></blockquote><blockquote type="cite"><br></blockquote><blockquote type="cite">The call to @bar can insert an arbitrarily complex amount of code, including<br></blockquote><blockquote type="cite">invokes, llvm.eh.selector calls, etc. Because there is no relationship between<br></blockquote><blockquote type="cite">the invoke of @foo and %eh_sel in ppad, we lose that information at ppad, which<br></blockquote><blockquote type="cite">is where we need it.<br></blockquote><br>It would of course be wrong to expect eh.exception to return the original value<br>in ppad if you inlined an invoke via the call to @bar, and reached %ppad via the<br>unwind branch of that invoke because a new exception was thrown. This is not a<br>problem. Here's how gcc does it. In fact llvm-gcc does exactly the same thing!<br>In lpad gcc grabs the exception and selector using the equivalent of<br>eh.exception and eh.selector and stashes the values in local variables. It<br>then uses those stashed variables everywhere, for example in ppad to do the<br>comparisons with eh.typeid.for etc. It doesn't try to get the value via<br>eh.exception in ppad. Since presumably you know this (since llvm-gcc does it)<br>maybe you were talking about something else?<br><br><blockquote type="cite">The code in DwarfEHPrepare::MoveExceptionValueCalls that moves the call to<br></blockquote><blockquote type="cite">llvm.eh.exception into the landing pad, and which you want to do for<br></blockquote><blockquote type="cite">llvm.eh.selector as well, will only complicate matters. It would introduce PHI<br></blockquote><blockquote type="cite">nodes for llvm.eh.selector values like it currently does for llvm.eh.exception<br></blockquote><blockquote type="cite">values.<br></blockquote><br>Sure, but that's not a problem because the catch info is only needed at invokes,<br>there is no need to go searching for it downstream, and I'm not sure why you<br>think there is such a need.<br><br></div></blockquote><div>In order to generate the EH tables correctly, we need to know what types can be thrown, where to land for a specific throw site, and how to branch to the correct catch handler. When you divorce the point where you branch to the catch handler from the point where it throws, you now have a huge gap that cannot be easily recovered from, and may be impossible to recover from.</div><div><br></div><div>This is the code that G++ generates from the example in my proposal:</div><div><br></div><div><div>LEHB2:</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>call<span class="Apple-tab-span" style="white-space:pre"> </span>__Z3foov</div><div>LEHE2:</div><div>. . .</div><div><div>L24:</div><div><span class="Apple-tab-span" style="white-space:pre"> </span># basic block 10</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>movq<span class="Apple-tab-span" style="white-space:pre"> </span>%rax, %r12</div><div>L5:</div><div><span class="Apple-tab-span" style="white-space:pre"> </span># basic block 11</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>movl<span class="Apple-tab-span" style="white-space:pre"> </span>%edx, %ebx</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>leaq<span class="Apple-tab-span" style="white-space:pre"> </span>-18(%rbp), %rdi</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>call<span class="Apple-tab-span" style="white-space:pre"> </span>__ZN1BD1Ev</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>movslq<span class="Apple-tab-span" style="white-space:pre"> </span>%ebx,%rdx</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>jmp<span class="Apple-tab-span" style="white-space:pre"> </span>L7</div><div>. . .</div><div><div>L7:</div><div><span class="Apple-tab-span" style="white-space:pre"> </span># basic block 15</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>movl<span class="Apple-tab-span" style="white-space:pre"> </span>%edx, %ebx</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>leaq<span class="Apple-tab-span" style="white-space:pre"> </span>-17(%rbp), %rdi</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>call<span class="Apple-tab-span" style="white-space:pre"> </span>__ZN1AD1Ev</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>movslq<span class="Apple-tab-span" style="white-space:pre"> </span>%ebx,%rdx</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>jmp<span class="Apple-tab-span" style="white-space:pre"> </span>L19</div></div><div>. . .</div><div><div>L19:</div><div><span class="Apple-tab-span" style="white-space:pre"> </span># basic block 19</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cmpq<span class="Apple-tab-span" style="white-space:pre"> </span>$3, %rdx</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>je<span class="Apple-tab-span" style="white-space:pre"> </span>L9</div><div><span class="Apple-tab-span" style="white-space:pre"> </span># basic block 20</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>cmpq<span class="Apple-tab-span" style="white-space:pre"> </span>$2, %rdx</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>jne<span class="Apple-tab-span" style="white-space:pre"> </span>L30</div><div><span class="Apple-tab-span" style="white-space:pre"> </span># basic block 21</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>jmp<span class="Apple-tab-span" style="white-space:pre"> </span>L39</div></div><div>. . .</div></div><div>L39: # The catch handler</div><div>. . .</div><div><div>GCC_except_table0:</div><div>LLSDA4:</div></div><div>. . .</div></div><div> <span class="Apple-style-span" style="white-space: pre; ">.set L$set$6,LEHB2-LFB4
.long L$set$6</span><span class="Apple-style-span" style="white-space: pre; "><span class="Apple-tab-span" style="white-space:pre"> </span></span><span class="Apple-style-span" style="white-space: pre; "># region 2 start
.set L$set$7,LEHE2-LEHB2
.long L$set$7</span><span class="Apple-style-span" style="white-space: pre; "><span class="Apple-tab-span" style="white-space:pre"> </span></span><span class="Apple-style-span" style="white-space: pre; "># length
.set L$set$8,L24-LFB4
.long L$set$8</span><span class="Apple-style-span" style="white-space: pre; "><span class="Apple-tab-span" style="white-space:pre"> </span></span><span class="Apple-style-span" style="white-space: pre; "># landing pad
.byte 0x7</span><span class="Apple-style-span" style="white-space: pre; "><span class="Apple-tab-span" style="white-space:pre"> </span></span><span class="Apple-style-span" style="white-space: pre; "># uleb128 0x7; action</span> <div>. . .</div><div><div><span class="Apple-tab-span" style="white-space:pre"> </span>.byte<span class="Apple-tab-span" style="white-space:pre"> </span>0x1<span class="Apple-tab-span" style="white-space:pre"> </span># Action record table</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>.byte<span class="Apple-tab-span" style="white-space:pre"> </span>0x0</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>.byte<span class="Apple-tab-span" style="white-space:pre"> </span>0x2</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>.byte<span class="Apple-tab-span" style="white-space:pre"> </span>0x7d</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>.byte<span class="Apple-tab-span" style="white-space:pre"> </span>0x3</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>.byte<span class="Apple-tab-span" style="white-space:pre"> </span>0x7d</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>.byte<span class="Apple-tab-span" style="white-space:pre"> </span>0x0</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>.byte<span class="Apple-tab-span" style="white-space:pre"> </span>0x7d</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>.align 2</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>.long<span class="Apple-tab-span" style="white-space:pre"> </span>__ZTIi+4@GOTPCREL</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>.long<span class="Apple-tab-span" style="white-space:pre"> </span>__ZTIPKc+4@GOTPCREL</div><div><span class="Apple-tab-span" style="white-space:pre"> </span>.long<span class="Apple-tab-span" style="white-space:pre"> </span>0</div><div><br></div></div></div><div>If the call to __Z3foov throws, we need to set up the tables to that it knows that it needs to call the __ZN1BD1Ev and __ZN1AD1Ev cleanups. This information requires looking at the invoke instruction – i.e., "where should I land?". It also needs to know which types it can catch in order to get the "action" variable.</div><div><br></div><div>So the information is needed at the invoke site.</div><div><br></div><div>The information is also needed at the site that makes the decision of which catch handler to execute (L19 in the above example). For one, it needs to know the action record table entries. And of course it needs to know the types that can catch, the personality function, and information about any filters. In your model, that point in the code is completely, and potentially irreversibly, separated from the invoke instruction.</div><div><br></div><div>This is why I abandoned my original idea. There was no good way of modeling a relationship between the invoke and the catch handler decision site in the IR.</div><br><blockquote type="cite"><div><blockquote type="cite"><blockquote type="cite">invoke void @_Z3foov()<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">to label %"3" unwind label %lpad personality @__gxx_personality_v0<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">catches %struct.__fundamental_type_info_pseudo* @_ZTIi,<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">%struct.__pointer_type_info_pseudo* @_ZTIPKc, i8* null<br></blockquote></blockquote><blockquote type="cite"><br></blockquote><blockquote type="cite">The use of "i8* null" here is just as bad as it is for the current<br></blockquote><blockquote type="cite">llvm.eh.selector call. There's no way to determine from this list whether the<br></blockquote><blockquote type="cite">last value is truly the catchall value or for a catch handler.<br></blockquote><br>There is a catch-all here only because there is a catch-all in the original<br>code:<br><br> } catch (...) {<br> printf("catchall\n");<br> }<br><br>In my proposal you don't need to know about catch-all, add special catch-alls<br>etc. If there was a catch-all in the original code then there is one on the<br>invoke, otherwise there is not. There is no special treatment of catch-all.<br><br></div></blockquote><div>You miss the point. As you well know, we need to know the specific type that is used for a catchall. E.g., i8* null in C++ and a global variable in Ada. In your code above, the i8* null is indistinguishable from the other types.</div><br><blockquote type="cite"><div><blockquote type="cite"><blockquote type="cite">"10": ; preds = %"5"<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">%exc_ptr31 = call i8* @llvm.eh.exception()<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">%filter32 = call i32 @llvm.eh.selector()<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">invoke void @_ZN1CD1Ev(%struct.A* %memtmp)<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">to label %"11" unwind label %fail personality @__gxx_personality_v0<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">catches i32 1 ; <- this is an empty filter, i.e. one that catches everything<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><br></blockquote></blockquote><blockquote type="cite">Filter? What do you mean by this?<br></blockquote><br><a href="http://llvm.org/docs/ExceptionHandling.html#throw_filters">http://llvm.org/docs/ExceptionHandling.html#throw_filters</a><br><br></div></blockquote><div>Why is it in a "catches" clause?</div><br><blockquote type="cite"><div><blockquote type="cite"><blockquote type="cite">Will everything work?<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">---------------------<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">I am confident that it will work fine, for a very simple reason: this is exactly<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">what gcc does! Of course it is in disguise, a wolf in sheep's clothing some<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">might say :) In fact moving closer to gcc like this is probably the best way<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">to be sure that exception handling works properly, since gcc is what everyone<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">tests against whether we like it or not (for example libstdc++ exploits some<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">details of how gcc implements exception handling that are not specified by the<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">standard, i.e. are implementation defined, and this has caused trouble for LLVM<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">in the past).<br></blockquote></blockquote><blockquote type="cite"><br></blockquote><blockquote type="cite">I would suspect that GCC has proper EH table generation mostly because it keeps<br></blockquote><blockquote type="cite">tables on the side; whereas we do not and cannot. Our current EH tables are<br></blockquote><blockquote type="cite">pretty poor. I would love to be able to generate tables similar to theirs.<br></blockquote><br>I think you are reading more into the gcc tables than actually exists. The<br>tables hold a set of nested regions. Each region consists of a set of basic<br>blocks. There are various types of regions, corresponding to handlers, filters,<br>cleanups etc. Given a basic block, what happens when an exception is thrown?<br>You wind up through the enclosing regions, from inner-most to outer-most looking<br>for what to do. If nothing matches then the exception unwinds out of the<br>function, otherwise the action specified by the region is taken.<br><br></div></blockquote><div>This is exactly what my newest EH model proposal is meant to do.</div><br><blockquote type="cite"><div>Here is an equivalent way of storing regions, by attaching them to basic blocks:<br>given a basic block BB, consider all regions that contain BB, and attach their<br>info to BB in order of inner-most region to outer-most region. That's what<br>my "catch info" does - and I think it contains all relevant info from the gcc<br>regions. If so, we have all the same info gcc has, so if gcc can do something<br>then so can we. You might object that regions can contain multiple basic<br>blocks, and by attaching info to basic blocks (currently this means to invokes)<br>you no longer can tell if two basic blocks are in the same region or not. This<br>is true to some extent (you can reconstruct maximal regions by comparing catch<br>info on basic blocks) but I don't think it matters for anything, in gcc it is<br>just an optimization to reduce memory usage and duplicated effort.<br><br><blockquote type="cite"><blockquote type="cite">I hate the way dwarf typeinfos, catches and filters are being baked into the<br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite">IR. Maybe metadata (see above) helps with this.<br></blockquote></blockquote><blockquote type="cite"><br></blockquote><blockquote type="cite">Metadata cannot be counted on to remain.<br></blockquote><br>Is that also true for global metadata used as an argument to an intrinsic?<br></div></blockquote><div><br></div><div>You will need to read the documentation. But Chris never expects metadata to stick around. In fact, it's derived from Value, not Use. So how can you have a use of it in an intrinsic that the compiler would know about?</div><br><blockquote type="cite"><div>Do you have an idea for how to keep catches etc out of the definition of the<br>IR? I'm worried that if one day we add support for, say, SEH then we will<br>have to change how the IR is defined again, and that's better avoided.<br><br></div></blockquote><div>I haven't given it a lot of thought. I don't like encoding DWARF-specific concepts into the IR. But my proposal doesn't do that either. It involves generic concepts that could be applied to all forms of EH.</div><br><blockquote type="cite"><div><blockquote type="cite">How will your implementation allow us to remove the Horrible Hack from<br></blockquote><blockquote type="cite">DwarfEHPrepare.cpp? Right now we catch and throw at almost every level that the<br></blockquote><blockquote type="cite">exception can propagate up. How will your proposal solve this?<br></blockquote><br>The horrible hack is not needed at all, pushing extra catch-alls is not needed<br>at all - it all goes away. Why were these needed? They were needed to handle<br>the effects of inlining, in particular that right now when you inline through<br>an invoke the catch info (contained in the selector) gets attached to the<br>inlined _Unwind_Resume which is far away from the place you really want it: you<br>want it on the inlined invoke that the _Unwind_Resume is downstream of. But<br>notice how inlining works with my scheme (described in my original proposal):<br>when inlining through an invoke, the catch info for that invoke gets appended<br>to everything you inline, including the invoke you inline. Thus it occurs in<br>the right place automatically! It also gets attached to the _Unwind_Resume,<br>which is also correct. If _Unwind_Resume is replaced with unwind (rewind in<br>my original proposal, since amended) then you can just replace unwind with a<br>branch and everything comes out in the wash (it is not obvious that everything<br>comes out in the wash, but nonetheless it does!).<br><br></div></blockquote><div>John all ready mentioned problems with your inlining proposal. Here is the code that brought up the need for what you instantly labeled a "horrible hack". It must work flawlessly with your new proposal.</div><div><br></div><div><span class="Apple-tab-span" style="white-space:pre"> </span><a href="http://llvm.org/viewvc/llvm-project?view=rev&revision=99670">http://llvm.org/viewvc/llvm-project?view=rev&revision=99670</a></div></div><div><span class="Apple-style-span" style="font-family: monospace; white-space: pre-wrap; -webkit-border-horizontal-spacing: 1px; -webkit-border-vertical-spacing: 1px; "><br></span></div><div><span class="Apple-style-span" style="font-family: monospace; white-space: pre-wrap; -webkit-border-horizontal-spacing: 1px; -webkit-border-vertical-spacing: 1px; ">int main() {</span></div><div><span class="Apple-style-span" style="font-family: monospace; white-space: pre-wrap; -webkit-border-horizontal-spacing: 1px; -webkit-border-vertical-spacing: 1px; "></span><span class="Apple-style-span" style="font-family: monospace; white-space: pre-wrap; -webkit-border-horizontal-spacing: 1px; -webkit-border-vertical-spacing: 1px; "> try {</span></div><div><span class="Apple-style-span" style="font-family: Times; -webkit-border-horizontal-spacing: 1px; -webkit-border-vertical-spacing: 1px; "><pre class="vc_log" style="white-space: pre-wrap; word-wrap: break-word; "> throw new std::exception();
} catch (std::exception *e) {
throw e;
}
}
</pre><pre class="vc_log" style="white-space: pre-wrap; word-wrap: break-word; "><br></pre><pre class="vc_log" style="white-space: pre-wrap; word-wrap: break-word; ">And this code needs to give a sensible backtrace (on Darwin at least). It will crash libunwind because it was doing horrible things:</pre><div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; color: rgb(201, 37, 36); "><span style="color: #74492d">#import </span><Foundation/Foundation.h></div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; min-height: 13px; "><br></div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; "><span style="color: #b50da1">int</span> main (<span style="color: #b50da1">int</span> argc, <span style="color: #b50da1">const</span> <span style="color: #b50da1">char</span> * argv[]) {</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; "> NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; min-height: 13px; "><br></div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; "> <span style="color: #b50da1">@try</span> {</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; "> <span style="color: #b50da1">@throw</span> [NSException exceptionWithName:<span style="color: #c92524">@"TestException"</span> reason:<span style="color: #c92524">@"Test"</span> userInfo:<span style="color: #b50da1">nil</span>];</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; "> }</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; "> <span style="color: #b50da1">@catch</span> (NSException *e) {</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; "> <span style="color: #b50da1">@throw</span> e;</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; "> }</div><p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; min-height: 13.0px"> <br class="webkit-block-placeholder"></p><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; "> [pool drain];</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; "> <span style="color: #b50da1">return</span> <span style="color: #3a00d6">0</span>;</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Menlo; ">}</div></div><div><br></div></span></div><br><div>-bw</div><div><br></div></body></html>