This is an archive of the discontinued LLVM Phabricator instance.

PR19864: Revert r209764 in favor of the fix in r215766 (loop backedge jump attributed to confusing location when the last source line of a loop is inside a conditional)
Needs ReviewPublic

Authored by dblaikie on Aug 18 2014, 7:53 AM.

Details

Reviewers
aprantl
Summary

The original fix for PR19864 (r209764) indirectly caused the loop
backedge of a (for \ if \ x) construct to be attributed to the 'if' line
(rather than the 'x' line where it was previously). It did this by
changing the location of the cleanup for the 'if'.

This was confusing all around - it's not the job of the 'if' to decide
where a loop backedge is located, source-wise, it was just happening by
accident because no other location was being set*. Instead, if we
specifically set the location before the backedge is emitted, its
location is now independent of that of the if's cleanup (this change was
made in r215766). So now you don't get strange nexting behavior in your
debugger where you could go "for -> if -> x -> /if/ -> for" as the
backedge was attributed to the 'if' line. Now the backedge is attributed
to the 'for' so it looks more natural (for -> if -> x -> for -> if ->
... ).

That is, unless the 'if' has cleanup (dtors) to run. In which case those
lines are attributed to the 'x' line and, in some circumstances, the
debugger may 'next' to them.

It looks as though GCC (though I don't know about LLDB) will avoid
nexting onto the dtor line somehow (I'm guessing some kind of
heuristic, but I haven't experimented too carefully to see just how/when
it works) so, even though the dtor is attributed to the 'x' line, and if
you break in the dtor and 'next' up you reach the 'x' line, if you next
from the 'if' and the condition is false, you'll end up at the 'for'
line and the dtor will have already been executed.

This breaks down when both the natural dtor /and/ exception handling
cleanup are required. GCC's output places the EH cleanup code after the
non-exceptional dtor invocation codepath. Clang places the EH cleanup
code before the non-exceptional dtor. This difference seems to cause GDB
to not skip the dtor line when 'next'ing with Clang's debug info, which
is unfortunate. I'm not sure if it's better fixed in GDB or Clang.

In brief:

Given "for / if / x"
Without any dtors in the 'if' scope, no behavior change.
With dtors:
Previous behavior (if true): for -> if -> x -> if -> for -> ...

(if false): for -> if -> if -> for -> if -> if

New behavior (under GDB):
Without EH cleanup (if true): for -> if -> x -> for -> ...

(if false): for -> if -> for -> ...

With EH cleanup (eg: if x can throw), even if false:

for -> if -> x -> for -> ...

Diff Detail

Event Timeline

dblaikie updated this revision to Diff 12618.Aug 18 2014, 7:53 AM
dblaikie retitled this revision from to PR19864: Revert r209764 in favor of the fix in r215766 (loop backedge jump attributed to confusing location when the last source line of a loop is inside a conditional).
dblaikie updated this object.
dblaikie added reviewers: aprantl, chandlerc.
dblaikie added a subscriber: Unknown Object (MLST).
aprantl edited edge metadata.Aug 18 2014, 2:23 PM

I tested the patch with LLDB, and it takes the line table literally:

void body() { printf("body\n"); }
foo::operator bool() throw() { return false; }
foo::~foo() { printf("~foo\n"); }

* thread #1: tid = 0x2a38e9, 0x0000000100000d8d debug-info-line-if`main + 221 at debug-info-line-if.cpp:51, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100000d8d debug-info-line-if`main + 221 at debug-info-line-if.cpp:51
   48  	  // CHECK: call void @_ZN3fooD1Ev{{.*}}, !dbg [[DBG3:!.*]]
   49  	
   50  	  //#line 400
-> 51  	  if (foo f = foo())
   52  	    body(); // this might throw
   53  	
   54  	  // GDB currently steps from the 'foo f' line to the 'body()' line, even if 'f'
(lldb) 
Process 10528 stopped
* thread #1: tid = 0x2a38e9, 0x0000000100000dc9 debug-info-line-if`main + 281 at debug-info-line-if.cpp:52, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100000dc9 debug-info-line-if`main + 281 at debug-info-line-if.cpp:52
   49  	
   50  	  //#line 400
   51  	  if (foo f = foo())
-> 52  	    body(); // this might throw
   53  	
   54  	  // GDB currently steps from the 'foo f' line to the 'body()' line, even if 'f'
   55  	  // is false, because the call to 'f's dtor is attribute to the 'body()' line.
(lldb) 
~foo
Process 10528 stopped
* thread #1: tid = 0x2a38e9, 0x0000000100000dce debug-info-line-if`main + 286 at debug-info-line-if.cpp:78, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100000dce debug-info-line-if`main + 286 at debug-info-line-if.cpp:78
   75  	  // CHECK: [[DBG5]] = metadata !{i32 401, i32 0, metadata !{{.*}}, null}
   76  	
   77  	  //#line 404
-> 78  	}
(lldb)

I know there is no satisfying line number for the call to ~foo, but putting it on a line that may not even get executed feels wrong to me. If necessary, I'd rather associate it with line 0, which is what the standard "no such location" location for the debugger to ignore.

IMO, the best we can do is to use the end of the CompoundStmt as the cleanup location if there is one, and otherwise give up and use line 0.

if (c)
  foo(); // cleanups = line 0

if (c) {
}         // cleanups

if (c)
  foo();
else {
  bar();
}       // cleanups

+Eric - this was the thread about is_stmt, scope locations, etc, I was
talking about last week.

I'm generally in favor of plumbing is_stmt through the front end for
another reason as well: in general, looking at the front end we'll know
what's likely a "better" place to put a breakpoint. We can then use the
plumbing to make the experience good in general.

chandlerc resigned from this revision.Mar 29 2015, 1:26 PM
chandlerc removed a reviewer: chandlerc.

Dave, not sure there is anything else you need from me here, but if so, add me back and let me know. Until then, i'm moving this off my review queue.