[cfe-dev] Setting breakpoints before assignments or calls

David Blaikie via cfe-dev cfe-dev at lists.llvm.org
Thu Aug 23 13:43:44 PDT 2018


On Thu, Aug 23, 2018 at 1:00 PM Vedant Kumar <vsk at apple.com> wrote:

>
> On Aug 23, 2018, at 12:22 PM, David Blaikie <dblaikie at gmail.com> wrote:
>
> +all the usual debugger folks
>
> I reckon adding nops would be a pretty unfortunate way to go in terms of
> code size, etc, if we could help it. One alternative that might work and
> we've tossed it around a bit - is emitting more accurate "is_stmt" flags.
> In your first example, the is_stmt flag could be set on the bar() call
> instruction - and any breakpoint set on an instruction that isn't "is_stmt"
> could instead set a breakpoint on the statement that covers that
> instruction (the nearest previous is_stmt instruction*)
>
>
> I hadn't considered using "is_stmt" at all, thanks for bringing that up!
>
> Given the rule you've outlined, I'm not sure that the debugger could do a
> good job in the following scenarios:
>

Ah, one thing that might've been confusing is when I say "before" I mean
"before in the instruction stream/line table" not "before in the source
order".


>
>   1|  foo =
>   2|    bar();
>   3|  foo =    //< Given a breakpoint on line 3, would the debugger stop
> on line 2?
>   4|    bar();
>
>
Given the line table (assuming a sort of simple pseudocode):

  %x1 = call bar(); # line 2
  store %x1 -> @foo # line 1
  %x2 = call bar(); # line 4
  store %x2 -> @foo # line 3

We could group lines 1 and 2 as part of the same statement and lines 3 and
4 as part of the same statement - then when the line table is emitted, the
lexically (in the assembly, not in the source code) first instruction
that's part of that statement gets the "is_stmt" flag. So in this case line
2 and 4 would have "is_stmt", a debugger, when requested to set a
breakpoint on line 3 would look at the line table and say "well, I have
this location on line 3, but it's not the start of a statement/not a good
place to break, so I'll back up in the instruction stream (within a basic
block - so that might get tricky for conditional operators and the like)
until I find an instruction marked with "is_stmt" - and it would find the
second call instruction and break there. The debugger could make the UI
nicer (rather than showing line 4 when the user requested line 3) by
highlighting the whole statement (all lines attributed to instructions
between this "is_stmt" instruction and the next (Or the end of the basic
block)).

Does that make sense?


> or
>
>   1|  if (...)
>   2|    foo();
>   3|  else
>   4|    bar();
>   5|  baz =   //< Given a breakpoint on line 5, would the debugger stop on
> line 2, 4, or possibly either?
>   6|    func();
>
> Is there another way to apply and interpret "is_stmt" flags to resolve
> these sorts of ambiguities?
>
>
> The inlining case seems to me like something that could be fixed without
> changes to the DWARF, purely in the consumer - the consumer has the info
> that the call occurs on a certain line and when I ask to break on that line
> it could break on that first instruction in the inlined subroutine
> (potentially giving me an artificial view that makes it look like I'm at
> the call site and leting me 'step' (though a no-op) into the inlined
> function).
>
>
> Oh, that's a great point. Yes, there is a TAG_inlined_subroutine for
> "inline_me" which contains AT_call_file and AT_call_line. That's enough
> information to do the right thing on the debugger side.
>
> thanks,
> vedant
>
>
> * This is a bit problematic when a statement gets interleaved/mixed up
> with other statements - DWARF has no equivalent of "ranges" for describing
> a statement. Likely not a problem at -O0 anyway, though (because little
> interleaving occurs there).
>
> On Wed, Aug 22, 2018 at 2:01 PM Vedant Kumar via cfe-dev <
> cfe-dev at lists.llvm.org> wrote:
>
>> Hello,
>>
>> I'd like to improve the situation with setting breakpoints on lines with
>> assignments or inlinable calls. This email outlines problem areas, possible
>> solutions, and why I think emitting extra nops at -O0 might be the best
>> solution.
>>
>> # Problem 1: Assignments
>>
>> Counter to user expectation, a breakpoint on a line containing an
>> assignment is reached when the assignment happens, not before the r.h.s is
>> evaluated.
>>
>> ## Example: Can't step into bar()
>>
>>   1| foo = // Set a breakpoint here. Note that it's not possible to step
>> into bar().
>>   2|   bar();
>>
>> One solution is to set the location of the assignment to the location of
>> the r.h.s (line 2). The problem with this approach is that it becomes
>> impossible to set a breakpoint on line 1.
>>
>> Another solution is to emit a nop (on line 1) prior to emitting the
>> r.h.s, and to emit an artificial location on the assignment's store
>> instruction. This makes it possible to step to line 1 before line 2, and
>> prevents users from stepping back to line 1 after line 2.
>>
>> # Problem 2: Inlinable calls
>>
>> Instructions from an inlined function don't have debug locations within
>> the caller. This can make it impossible to set a breakpoint on a line that
>> contains a call to an inlined function.
>>
>> ## Example: Can't set a breakpoint on a call
>>
>> It's easier to see the bug via Godbolt: https://godbolt.org/z/scwF20.
>> Note that it's not possible to set a breakpoint on line 9 (on "inline_me").
>> At the IR-level, we do emit an unconditional branch with a location that's
>> on line 9, but we have to drop that branch during ISel.
>>
>> The only solution to this problem (afaik) is to insert a nop before
>> inlinable calls. In this example the nop would be on line 9.
>>
>> One alternative I've heard is to make the first inlined instruction look
>> like it's located within the caller, but that actually introduces a bug.
>> You wouldn't be able to set a breakpoint on the relevant location in the
>> inlined callee.
>>
>> # Proposal
>>
>> As outlined above, I think the best way to address issues with setting
>> breakpoints on assignments and calls is to insert nops with suitable
>> locations at -O0. These nops would lower to a target-specific nop at -O0,
>> and lower to nothing at -O1 or greater (like @llvm.donothing).
>>
>> The tentative plan is to introduce an intrinsic (say, @llvm.dbg.nop) to
>> accomplish this.
>>
>> I don't anticipate there being a substantial compile-time impact, but
>> haven't actually measured this yet. I'd like to get some feedback before
>> going forward. Let me know what you think!
>>
>> thanks,
>> vedant
>>
>>
>> _______________________________________________
>> cfe-dev mailing list
>> cfe-dev at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20180823/9f3ed59f/attachment.html>


More information about the cfe-dev mailing list