(original) (raw)
On Jun 24, 2015, at 12:12 PM, Alexey Samsonov <vonosmas@gmail.com> wrote:Hi Adrian,You might want to take a look at abandoned http://reviews.llvm.org/D2658, where I tried to implement something similar.Looks like we're now at the point where we \*do\* require complicated solution and analysis in DbgValueHistoryCalculator...On Tue, Jun 23, 2015 at 4:41 PM, Adrian Prantl <aprantl@apple.com> wrote:Here is a proposal for improving DbgValueHistoryCalculator and the
overall quality of debug locations.
Focus: This is about lowering the DBG\_VALUE machine instructions to
DWARF location lists.
Non-focus: This is not about (typical -O0) variables that permanently
reside at a frame index and are described with dbg.declare intrinsics
in the IR. These variables are stored in the MMI side-table and
processed separately.
The semantics of DBG\_VALUE
==========================
Examples:
DBG\_VALUE %EAX, %noreg, !"a", <<0x7fd4bbc17470>>; line no:3
DBG\_VALUE %RBX, 56, !"a", <<0x7ffd5ac3b3c0>>; line no:4 indirect
DBG\_VALUE 0, 0, !"a", <<0x7fd4bbc17470>>; line no:3
The DBG\_VALUE machine instruction informs us that a variable (or a
piece of it) is henceforth stored in a specific location or has a
constant value. This is valid until the next DBG\_VALUE instruction
describing the same variable or the location is being clobbered.
Lowering today
==============
1\. DbgValueHistoryCalculator takes the MachineFunction graph and
produces a list of live ranges for each variable (the
DbgValueHistoryMap).
Note: Variable live ranges are to be consumed by a debugger. They
refer to the physical addresses and are agnostic of control flow.
The live ranges produced by DbgValueHistoryCalculator are pairs of
MachineInstructions, the first of which is always the DBG\_VALUE
describing the variable and location.
The ranges are calculated as follows:
- If the location is a register the range extends until the
register is clobbered or until the end of the basic block.
- If the location is a constant the range extends until the next
DBG\_VALUE instruction describing the same variable.
- If the location is indirect and the register is not clobbered
outside the function prologue and epilogue the the range is the
entire function. This is a heuristic to make stack
frame-allocated variables work better. Otherwise the range
extends until the next DBG\_VALUE or the end of the basic block.
2\. The buildLocationList() function takes the list of ranges of one
variable and builds a location list. A variable may consist of many
pieces which have their own live ranges. A live ranges for a piece
is split up so that no piece's live range starts or ends in the
middle of another piece's live range. Any two consecutive ranges
with identical location contents are merged. Labels are requested
for start and end of each range.
3\. The location list is finalized by lowering it into DWARF and
emitted into a buffer.
Shortcomings with the current apporach
======================================
Problems with the current approach include inaccurate live ranges for
constant values, ranges ending too early (usually at basic block
boundaries), poor handling of frame-register-indirect variables that
were introduced by spill code, poor handling of variables that are in
more than one location at once.
A better DbgValueHistoryCalculator
==================================
Currently DbgValueHistoryCalculator does a very simple, linear pass
through all MachineInstructions, with a couple of heuristics to make
common cases such as the frame index variables work.
It would be possible to address many of the current problems by having
earlier passes emit many more DBG\_VALUE instructions. There are
several problem with this approach though: It distributes the
complexity across many more passes and thus imposes a maintenance
burden on authors of prior passes, increases machine code size, and
stores a lot of redundant information.
What I'm proposing instead is to make DbgValueHistoryCalculator
smarter at creating ranges. The goal is to make hack^H^H^Heuristics such as
the frame index handling unnecessary by correctly propagating DBG\_VALUE
liveness across basic block boundaries.
To illustrate what I mean I'll use a notation where
DbgValueHistoryCalculator is defined in terms of a data-flow analysis.
First a couple of data types used in the following pseudo code:
- A range is a pair of MachineInstructions (start, end) that both belong
to the same basic block.
- range\[var\] is the final list of live ranges for a variable.How come you never remove ranges from range\[var\]? What if you have smth. like
BB1:DBG\_VALUE %RAX, %noreg, !"a"jmp BB2BB2:// more minsnsclobber %RAXBB3:clobber %RAXDBG\_VALUE %RBX, %noreg, !"a"jmp BB2
Looks like you would handle BB1, then BB2, and create a range for "a" from the beginning of BB2 to clobber instruction.But this might be incorrect - if we have jumped to BB2 from BB3, then the location of "a" is anything but %RAX.
To guarantee termination new ranges are only committed to ranges\[var\] when we know they are valid on all paths.
BB1 is visited first and we record a range for a from the DBG\_VALUE to the end of BB1:
ranges\[a\].push\_back( (DBG\_VALUE, BB2.first\_insn) )
(There’s a bug in the pseudo-code doesn’t set the end of the range correctly, but the comment conveys the intention.)
When BB2 is visited the data-flow analysis magic kicks in via the join() function, which I (as Paul pointed out) left out from the pseudo-code. Sorry for the extra confusion. Next time I write up such a proposal I should just implement it so I can make sure that all the pieces are actually there and I don’t just imagine them :-)
The first time BB2 is visited, we only have the information from BB1, so join(BB2, /\*pred1\*/ BB1, /\*pred2\*/ BB3) returns an empty set. After BB3 is visited join() will still come back with an empty set because the intersection of the location for “a” coming from BB1 and BB3 is different.
I think you should respect the control flow somehow, and consider locations of "a" at the beginning of basic block BB onlyif you are certain that locations of "a" at the end of all BB's predecessors don't contradict.
It does take the control flow into account, but I left out the important part from the pseudo-code and replaced it with a hand-wavy statement about it being a data-flow analysis. Sorry about that!
- open\_ranges\[BB\]\[var\] is the list of not-yet-terminated ranges forvar inside the current basic block BB.
- outgoing\_loc\[BB\]\[var\] is the list of locations for var valid at
the end of a basic block BB.
// This code is probably buggy because I didn't run it through a
// compiler yet, but I hope it serves to illustrate my point.
// Visit a machine instruction.I'm kind of concerned about the runtime cost of this. Looks like this can be at leastO(number\_of\_minsn \* number\_of\_local\_variables), which is already significant. And it seemsthat you can iterate over the same basic block several times.
Yes, we’ll have to benchmark it very carefully before deciding that the extra complexity is worth it.
thanks for all the feedback
-- adrian
transfer(MInsn) {
// A DBG\_VALUE marks the beginning of a new range.
if (MInsn is a DBG\_VALUE(var, loc)) {
for ((rvar, start) : open\_ranges\[BB\])
// A DBG\_VALUE terminates a range started by a previous
// DBG\_VALUE for the same variable, if the described pieces
// overlap.
if (var == rvar && piece\_overlaps(MInsn, start)) {
ranges\[var\].push\_back((start, MInsn));
open\_ranges\[BB\]\[rvar\].remove(start));
}
open\_ranges\[BB\]\[var\].push\_back(MInsn)
}
// A def of a register may mark the end of a range.
if (MInsn is a def(reg)) {
for ((var, start) : open\_ranges\[BB\])
if (start.loc == reg) {
ranges\[var\].push\_back((start, MInsn));
open\_ranges\[BB\]\[var\].remove(start));
}
// End all ranges in the current basic block.
if (MInsn.isTerminator())
for (range : open\_ranges\[BB\]) {
ranges\[var\].push\_back(range);
open\_ranges\[BB\].remove(range);
outgoing\_loc\[BB\]\[range.var\].push\_back(range.loc);
changed = true;
}
}
// Visit the beginning of a basic block BB with two predecessors.
join(BB, BB1, BB2) {
for ((var1, loc1) : outgoing\_loc\[BB1\])
for (loc2 : outgoing\_loc\[BB2\]\[var1\])
// It's only safe to propagate a range if all predecessors end
// with the same location.
if (loc1.kind == loc2.kind &&
loc1.val == loc2.val)
// Not sure how to best implement this.
open\_ranges\[BB\]\[var\].push\_back(new DBG\_VALUE(loc1));Yep, that's kind of problem, we can't modify MachineFunction at this point.}
analyze(MF) {
for (BB : MF)
workset.push\_back(BB);
while (!workset.empty()) {
changed = false;
for (MI : workset.pop\_front())
transfer(MI)
if (changed)
workset.append(BB.successors())
}
}
A couple of observations about the pseudo-code above: The analysis
terminates, because ranges is write-only, making this effectively a
bit-vector problem. It is also safe, because we are only propagating
ranges into the next basic block if all predecessors have the same
outgoing location for a variable piece.
One problem that is not addressed by the approach above is how to
become better at handling variables that are in more than one location
at once: As Keno noted on llvm-commits recently, the fundamental
problem is that DbgValueHistoryCalculator cannot safely distinguish
between a DBG\_VALUE that provides an additional valid location and one
describing an updated location for a variable. IMO this is best
addressed on a case-by-case basis in the pass that introduces the second
DBG\_VALUE, either by marking it as alternative location or not emitting
it at all, but I’m open for suggestions.
Comments?
-- adrian
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
LLVM Developers mailing list
LLVMdev@cs.uiuc.edu http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev--Alexey Samsonov
vonosmas@gmail.com