[llvm-dev] BoF: Debug info for optimized code.
Adrian Prantl via llvm-dev
llvm-dev at lists.llvm.org
Thu Nov 10 16:13:25 PST 2016
> On Nov 10, 2016, at 2:30 PM, Hal Finkel via llvm-dev <llvm-dev at lists.llvm.org> wrote:
> ----- Original Message -----
>> From: "Paul via llvm-dev Robinson" <llvm-dev at lists.llvm.org>
>> To: llvm-dev at lists.llvm.org
>> Sent: Thursday, November 10, 2016 4:07:06 PM
>> Subject: Re: [llvm-dev] BoF: Debug info for optimized code.
>> At the BoF session, Reid Kleckner wrote a few notes on the whiteboard
>> and then I got a photo of it before the next session started up.
>> transcribed those notes here, and expanded on them a bit with my own
>> thoughts. If anybody else has notes/thoughts, please share them.
>> Whiteboard notes
>> Variable info metrics
>> - Induction variable tracking
>> - Contrast -O0 vs -O2 variables, breakpoint locations
>> - Track line info for side effects only
>> (semantic stepping) "key" instructions
Thanks for writing this up, Paul, and thanks everyone who participated in the session! I found it to be very a productive discussion.
>> Unpacking that a bit...
>> Induction variable tracking
>> Somebody (Hal?) observed that in counted loops (I = 1 to N) the
> Yes, it was me. It was pointed out (in conversations after the BoF) that we already have some pass (SROA?) that builds expressions for things; but that's pretty limited.
Yes that was SROA. There is also a patch lying around in review limbo that does a similar thing for the type legalizer.
> We'll need utilities to build more-general expressions (and maybe some kind of SCEV visitor to build them), and also for full generality, debug intrinsics that take multiple value operands so that we can write DWARF expressions that refer to multiple values (which is currently not possible).
To expand on this, the problem is that we cannot refer to IR from metadata, so in order to support this we could, for example, extend llvm.dbg.value() to accept multiple operands:
; Straw man syntax for calculating *(ptr+ofs).
; The first argument is pushed implicitly, for the subsequent ones we'll need a placeholder.
call @llvm.dbg.value(metadata i64* %ptr, metadata i64 %ofs, i64 0,
!DIExpression(DW_OP_LLVM_push_arg, DW_OP_plus, DW_OP_deref))
or something like that.
> Thanks again,
>> often gets transformed into something else more useful (e.g. an
>> instead of an index). DWARF is powerful enough to express how to
>> the original counter value, if only the induction transformation had
>> a way
>> to describe what it did (or more precisely, how to recover the
>> value after what it did).
>> Contrast -O0 vs -O2 variables, breakpoint locations
>> This came up during a discussion on debug-info-quality
>> One metric for quality of debug info of optimized code is to compare
>> is "available" at -O0 to what what is "available" at -O2. This can
>> applied to both kinds of debug info affected by optimizations:
>> whether a
>> variable is available (has a defined location) and whether a
>> is available (the line has a defined "is-a-statement" address).
>> If you look at the set of instructions where a variable has a valid
>> location, how does that set compare to the set of instructions for
>> lexical scope that contains the variable? If you look at the sets of
>> breakpoint locations described by the line table, how does the set
>> -O2 compare to the set for -O0?
>> It's not hard to imagine tooling that would permit comparisons of
>> kind, and some people have had tooling like that in previous jobs.
>> Track line info for side effects only
>> (aka semantic stepping or "key" instructions)
>> This idea is based on two observations:
>> (1) Optimization tends to shuffle instructions around, so that you
>> up with instructions "from" a given source line being mixed in
>> instructions "from" other source lines. If we very precisely
>> the source line for every instruction, then single-stepping
>> "the source" in a debugger becomes very back-and-forth and
>> a good place to set a breakpoint on "the line" becomes a dicey
>> (2) If you look at the set of instructions generated for a given
>> it's easy to conclude that "some are more equal than others."
>> means for something like a simple assignment, the load is kind of
>> important, the ZEXT not so much, and the store is really the
>> So, picking and choosing which instructions to mark as good stopping
>> places could well improve the user-experience without significantly
>> interfering with the user's ability to see what their program is
>> [Okay, I'm really going beyond what we said in the BoF, but I think
>> a worthwhile point to expand upon.]
>> Let's unpack an assignment from an 'unsigned short' to an 'unsigned
>> as an example. This basically turns into a load/ZEXT/store sequence.
>> If you have an optimization that hoists the load+ZEXT above an 'if'
>> loop-top, but leaves the store down inside the 'then' part or loop
>> is it really important to tag the load+ZEXT with the original source
>> line? If you want to stop on "the line," doing it just before the
>> is really the critical thing.
>> That is, the store is the "key" or "semantically significant"
>> here, and the load/ZEXT are not so important. You can have a smooth,
>> user-friendly debugging experience if you mark the store as a good
>> stopping point for that statement, and don't mark the load/ZEXT that
>> (even though, pedantically, the load/ZEXT are also "from" the same
>> Now, how far you take this idea and in what circumstances is arguable
>> because it very quickly is in the arena of human-factors quality, and
>> people may differ in their preferences for "precise" versus "smooth"
>> single-stepping or breakpoint-location experience. But these things
>> definitely have an effect on the experience and we have to be willing
>> to trade off one for the other in some cases.
One topic that also came up in the discussion (after the?) session was the interest in making -O1 only enable optimizations that are known not to have an adverse effect on debuggability or even introducing a dedicated -Og mode like GCC has.
More information about the llvm-dev