Skip to content

Commit

Permalink
[LiveDebugVariables] Use lexical scope to trim debug value live inter…
Browse files Browse the repository at this point in the history
…vals

The debug value live intervals computed by Live Debug Variables may extend
beyond the range of the debug location's lexical scope. In this case,
splitting of an interval can result in an interval outside of the scope being
created, causing extra unnecessary DBG_VALUEs to be emitted. To prevent this,
trim the intervals to the lexical scope.

This resolves PR33730.

Reviewers: aprantl

Differential Revision: https://reviews.llvm.org/D35953

llvm-svn: 309933
  • Loading branch information
Robert Lougher committed Aug 3, 2017
1 parent 2c43dce commit 10f740d
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 11 deletions.
97 changes: 90 additions & 7 deletions llvm/lib/CodeGen/LiveDebugVariables.cpp
Expand Up @@ -21,7 +21,9 @@

#include "LiveDebugVariables.h"
#include "llvm/ADT/IntervalMap.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/LexicalScopes.h"
#include "llvm/CodeGen/LiveIntervalAnalysis.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFunction.h"
Expand Down Expand Up @@ -101,6 +103,10 @@ class UserValue {
/// Map of slot indices where this value is live.
LocMap locInts;

/// Set of interval start indexes that have been trimmed to the
/// lexical scope.
SmallSet<SlotIndex, 2> trimmedDefs;

/// coalesceLocation - After LocNo was changed, check if it has become
/// identical to another location, and coalesce them. This may cause LocNo or
/// a later location to be erased, but no earlier location will be erased.
Expand Down Expand Up @@ -229,7 +235,7 @@ class UserValue {
/// computeIntervals - Compute the live intervals of all locations after
/// collecting all their def points.
void computeIntervals(MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI,
LiveIntervals &LIS);
LiveIntervals &LIS, LexicalScopes &LS);

/// splitRegister - Replace OldReg ranges with NewRegs ranges where NewRegs is
/// live. Returns true if any changes were made.
Expand Down Expand Up @@ -627,10 +633,9 @@ UserValue::addDefsFromCopies(LiveInterval *LI, unsigned LocNo,
}
}

void
UserValue::computeIntervals(MachineRegisterInfo &MRI,
const TargetRegisterInfo &TRI,
LiveIntervals &LIS) {
void UserValue::computeIntervals(MachineRegisterInfo &MRI,
const TargetRegisterInfo &TRI,
LiveIntervals &LIS, LexicalScopes &LS) {
SmallVector<std::pair<SlotIndex, unsigned>, 16> Defs;

// Collect all defs to be extended (Skipping undefs).
Expand Down Expand Up @@ -672,17 +677,88 @@ UserValue::computeIntervals(MachineRegisterInfo &MRI,
extendDef(Idx, LocNo, LR, VNI, nullptr, LIS);
}

// Finally, erase all the undefs.
// Erase all the undefs.
for (LocMap::iterator I = locInts.begin(); I.valid();)
if (I.value() == ~0u)
I.erase();
else
++I;

// The computed intervals may extend beyond the range of the debug
// location's lexical scope. In this case, splitting of an interval
// can result in an interval outside of the scope being created,
// causing extra unnecessary DBG_VALUEs to be emitted. To prevent
// this, trim the intervals to the lexical scope.

LexicalScope *Scope = LS.findLexicalScope(dl);
if (!Scope)
return;

SlotIndex PrevEnd;
LocMap::iterator I = locInts.begin();

// Iterate over the lexical scope ranges. Each time round the loop
// we check the intervals for overlap with the end of the previous
// range and the start of the next. The first range is handled as
// a special case where there is no PrevEnd.
for (const InsnRange &Range : Scope->getRanges()) {
SlotIndex RStart = LIS.getInstructionIndex(*Range.first);
SlotIndex REnd = LIS.getInstructionIndex(*Range.second);

// At the start of each iteration I has been advanced so that
// I.stop() >= PrevEnd. Check for overlap.
if (PrevEnd && I.start() < PrevEnd) {
SlotIndex IStop = I.stop();
unsigned LocNo = I.value();

// Stop overlaps previous end - trim the end of the interval to
// the scope range.
I.setStopUnchecked(PrevEnd);
++I;

// If the interval also overlaps the start of the "next" (i.e.
// current) range create a new interval for the remainder (which
// may be further trimmed).
if (RStart < IStop)
I.insert(RStart, IStop, LocNo);
}

// Advance I so that I.stop() >= RStart, and check for overlap.
I.advanceTo(RStart);
if (!I.valid())
return;

if (I.start() < RStart) {
// Interval start overlaps range - trim to the scope range.
I.setStartUnchecked(RStart);
// Remember that this interval was trimmed.
trimmedDefs.insert(RStart);
}

// The end of a lexical scope range is the last instruction in the
// range. To convert to an interval we need the index of the
// instruction after it.
REnd = REnd.getNextIndex();

// Advance I to first interval outside current range.
I.advanceTo(REnd);
if (!I.valid())
return;

PrevEnd = REnd;
}

// Check for overlap with end of final range.
if (PrevEnd && I.start() < PrevEnd)
I.setStopUnchecked(PrevEnd);
}

void LDVImpl::computeIntervals() {
LexicalScopes LS;
LS.initialize(*MF);

for (unsigned i = 0, e = userValues.size(); i != e; ++i) {
userValues[i]->computeIntervals(MF->getRegInfo(), *TRI, *LIS);
userValues[i]->computeIntervals(MF->getRegInfo(), *TRI, *LIS, LS);
userValues[i]->mapVirtRegs(this);
}
}
Expand Down Expand Up @@ -957,6 +1033,13 @@ void UserValue::emitDebugValues(VirtRegMap *VRM, LiveIntervals &LIS,
SlotIndex Start = I.start();
SlotIndex Stop = I.stop();
unsigned LocNo = I.value();

// If the interval start was trimmed to the lexical scope insert the
// DBG_VALUE at the previous index (otherwise it appears after the
// first instruction in the range).
if (trimmedDefs.count(Start))
Start = Start.getPrevIndex();

DEBUG(dbgs() << "\t[" << Start << ';' << Stop << "):" << LocNo);
MachineFunction::iterator MBB = LIS.getMBBFromIndex(Start)->getIterator();
SlotIndex MBBEnd = LIS.getMBBEndIdx(&*MBB);
Expand Down
9 changes: 5 additions & 4 deletions llvm/test/DebugInfo/X86/dbg-value-dag-combine.ll
Expand Up @@ -5,10 +5,11 @@ target triple = "i686-apple-darwin"

; There should be a DEBUG_VALUE for each call to llvm.dbg.value

; CHECK: ##DEBUG_VALUE: __OpenCL_test_kernel:ip <-
; CHECK: ##DEBUG_VALUE: xxx <- 0
; CHECK: ##DEBUG_VALUE: gid <- %E{{..$}}
; CHECK: ##DEBUG_VALUE: idx <- %E{{..$}}
; CHECK-LABEL: __OpenCL_test_kernel:
; CHECK-DAG: ##DEBUG_VALUE: __OpenCL_test_kernel:ip <-
; CHECK-DAG: ##DEBUG_VALUE: xxx <- 0
; CHECK-DAG: ##DEBUG_VALUE: gid <- %E{{..$}}
; CHECK-DAG: ##DEBUG_VALUE: idx <- %E{{..$}}
; CHECK-NOT: ##DEBUG_VALUE:

declare <4 x i32> @__amdil_get_global_id_int()
Expand Down
77 changes: 77 additions & 0 deletions llvm/test/DebugInfo/X86/live-debug-variables.ll
@@ -0,0 +1,77 @@
; RUN: llc -mtriple=x86_64-linux-gnu -filetype=obj -o - %s | llvm-dwarfdump -debug-dump=loc - | FileCheck %s

; The test inlines the function F four times, with each inlined variable for
; "i4" sharing the same virtual register. This means the live interval of the
; register spans all of the inlined callsites, extending beyond the lexical
; scope of each. Later during register allocation the live interval is split
; into multiple intervals. Check that this does not generate multiple entries
; within the debug location (see PR33730).
;
; Generated from:
;
; extern int foobar(int, int, int, int, int);
;
; int F(int i1, int i2, int i3, int i4, int i5) {
; return foobar(i1, i2, i3, i4, i5);
; }
;
; int foo(int a, int b, int c, int d, int e) {
; return F(a,b,c,d,e) +
; F(a,b,c,d,e) +
; F(a,b,c,d,e) +
; F(a,b,c,d,e);
; }

; CHECK: Beginning address offset
; CHECK-NOT: Beginning address offset

declare i32 @foobar(i32, i32, i32, i32, i32)

define i32 @foo(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) !dbg !25 {
entry:
tail call void @llvm.dbg.value(metadata i32 %d, i64 0, metadata !15, metadata !17) #3, !dbg !41
%call.i = tail call i32 @foobar(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) #3, !dbg !43
%call.i21 = tail call i32 @foobar(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) #3, !dbg !50
%add = add nsw i32 %call.i21, %call.i, !dbg !51
%call.i22 = tail call i32 @foobar(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) #3, !dbg !58
%add3 = add nsw i32 %add, %call.i22, !dbg !59
%call.i23 = tail call i32 @foobar(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) #3, !dbg !66
%add5 = add nsw i32 %add3, %call.i23, !dbg !67
ret i32 %add5, !dbg !68
}

declare void @llvm.dbg.value(metadata, i64, metadata, metadata)

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5}
!llvm.ident = !{!6}

!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0 (trunk 308976)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "foo.c", directory: "")
!2 = !{}
!3 = !{i32 2, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 4}
!6 = !{!"clang version 6.0.0 (trunk 308976)"}
!7 = distinct !DISubprogram(name: "F", scope: !1, file: !1, line: 3, type: !8, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !11)
!8 = !DISubroutineType(types: !9)
!9 = !{!10, !10, !10, !10, !10, !10}
!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!11 = !{!15}
!15 = !DILocalVariable(name: "i4", arg: 4, scope: !7, file: !1, line: 3, type: !10)
!17 = !DIExpression()
!25 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 7, type: !8, isLocal: false, isDefinition: true, scopeLine: 7, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !26)
!26 = !{}
!38 = distinct !DILocation(line: 8, column: 10, scope: !25)
!41 = !DILocation(line: 3, column: 35, scope: !7, inlinedAt: !38)
!43 = !DILocation(line: 4, column: 10, scope: !7, inlinedAt: !38)
!45 = distinct !DILocation(line: 9, column: 10, scope: !25)
!50 = !DILocation(line: 4, column: 10, scope: !7, inlinedAt: !45)
!51 = !DILocation(line: 8, column: 23, scope: !25)
!53 = distinct !DILocation(line: 10, column: 10, scope: !25)
!58 = !DILocation(line: 4, column: 10, scope: !7, inlinedAt: !53)
!59 = !DILocation(line: 9, column: 23, scope: !25)
!61 = distinct !DILocation(line: 11, column: 10, scope: !25)
!66 = !DILocation(line: 4, column: 10, scope: !7, inlinedAt: !61)
!67 = !DILocation(line: 10, column: 23, scope: !25)
!68 = !DILocation(line: 8, column: 3, scope: !25)

0 comments on commit 10f740d

Please sign in to comment.