mirror of
https://github.com/Gericom/teak-llvm.git
synced 2025-06-23 05:25:50 -04:00
[DebugInfo] Don't create multiple DBG_VALUEs when sinking
This patch addresses a performance problem reported in PR43855, and present in the reapplication in in 001574938e5. It turns out that MachineSink will (often) move instructions to the first block that post-dominates the current block, and then try to sink further. This means if we have a lot of conditionals, we can needlessly create large numbers of DBG_VALUEs, one in each block the sunk instruction passes through. To fix this, rather than immediately sinking DBG_VALUEs, record them in a pass structure. When sinking is complete and instructions won't be sunk any further, new DBG_VALUEs are added, avoiding lots of intermediate DBG_VALUE $noregs being created. Differential revision: https://reviews.llvm.org/D70676
This commit is contained in:
parent
e4cdd62631
commit
30e8f80fd5
@ -15,6 +15,7 @@
|
|||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/DenseSet.h"
|
#include "llvm/ADT/DenseSet.h"
|
||||||
#include "llvm/ADT/PointerIntPair.h"
|
#include "llvm/ADT/PointerIntPair.h"
|
||||||
#include "llvm/ADT/SetVector.h"
|
#include "llvm/ADT/SetVector.h"
|
||||||
@ -127,6 +128,24 @@ namespace {
|
|||||||
/// current block.
|
/// current block.
|
||||||
DenseSet<DebugVariable> SeenDbgVars;
|
DenseSet<DebugVariable> SeenDbgVars;
|
||||||
|
|
||||||
|
/// A set of DBG_VALUEs, indexed by their DebugVariable. Refers to the
|
||||||
|
/// original / non-sunk DBG_VALUE, which should be cloned to the final
|
||||||
|
/// sunk location.
|
||||||
|
using SinkingVarSet = DenseMap<DebugVariable, MachineInstr *>;
|
||||||
|
|
||||||
|
/// Record of variable locations to re-create after the sinking of a
|
||||||
|
/// vreg definition completes.
|
||||||
|
struct SunkDebugDef {
|
||||||
|
SinkingVarSet InstsToSink; /// Set of DBG_VALUEs to recreate.
|
||||||
|
MachineInstr *MI; /// Location to place DBG_VALUEs.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Map sunk vreg to the final destination of the vreg def, and the
|
||||||
|
/// DBG_VALUEs that were attached to it. We need only create one new
|
||||||
|
/// DBG_VALUE even if the defining instruction sinks through multiple
|
||||||
|
/// blocks.
|
||||||
|
DenseMap<unsigned, SunkDebugDef> SunkDebugDefs;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static char ID; // Pass identification
|
static char ID; // Pass identification
|
||||||
|
|
||||||
@ -184,6 +203,11 @@ namespace {
|
|||||||
/// to the copy source.
|
/// to the copy source.
|
||||||
void SalvageUnsunkDebugUsersOfCopy(MachineInstr &,
|
void SalvageUnsunkDebugUsersOfCopy(MachineInstr &,
|
||||||
MachineBasicBlock *TargetBlock);
|
MachineBasicBlock *TargetBlock);
|
||||||
|
|
||||||
|
/// Re-insert any DBG_VALUE instructions that had their operands sunk
|
||||||
|
/// in this function.
|
||||||
|
void reinsertSunkDebugDefs(MachineFunction &MF);
|
||||||
|
|
||||||
bool AllUsesDominatedByBlock(unsigned Reg, MachineBasicBlock *MBB,
|
bool AllUsesDominatedByBlock(unsigned Reg, MachineBasicBlock *MBB,
|
||||||
MachineBasicBlock *DefMBB,
|
MachineBasicBlock *DefMBB,
|
||||||
bool &BreakPHIEdge, bool &LocalUse) const;
|
bool &BreakPHIEdge, bool &LocalUse) const;
|
||||||
@ -316,6 +340,33 @@ MachineSinking::AllUsesDominatedByBlock(unsigned Reg,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MachineSinking::reinsertSunkDebugDefs(MachineFunction &MF) {
|
||||||
|
// Re-insert any DBG_VALUEs sunk.
|
||||||
|
for (auto &Iterator : SunkDebugDefs) {
|
||||||
|
SunkDebugDef Def = Iterator.second;
|
||||||
|
unsigned VReg = Iterator.first;
|
||||||
|
|
||||||
|
MachineBasicBlock::iterator DestLoc = Def.MI->getIterator();
|
||||||
|
MachineBasicBlock *MBB = Def.MI->getParent();
|
||||||
|
|
||||||
|
// Place DBG_VALUEs immediately after the sunk instruction.
|
||||||
|
++DestLoc;
|
||||||
|
|
||||||
|
// Collect the DBG_VALUEs being sunk and put them in a deterministic order.
|
||||||
|
using VarInstPair = std::pair<DebugVariable, MachineInstr *>;
|
||||||
|
SmallVector<VarInstPair, 16> InstsToSink;
|
||||||
|
for (auto &SunkLoc : Def.InstsToSink)
|
||||||
|
InstsToSink.push_back(SunkLoc);
|
||||||
|
llvm::sort(InstsToSink);
|
||||||
|
|
||||||
|
for (auto &SunkLoc : InstsToSink) {
|
||||||
|
MachineInstr *NewDbgMI = MF.CloneMachineInstr(SunkLoc.second);
|
||||||
|
NewDbgMI->getOperand(0).setReg(VReg);
|
||||||
|
MBB->insert(DestLoc, NewDbgMI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool MachineSinking::runOnMachineFunction(MachineFunction &MF) {
|
bool MachineSinking::runOnMachineFunction(MachineFunction &MF) {
|
||||||
if (skipFunction(MF.getFunction()))
|
if (skipFunction(MF.getFunction()))
|
||||||
return false;
|
return false;
|
||||||
@ -366,6 +417,9 @@ bool MachineSinking::runOnMachineFunction(MachineFunction &MF) {
|
|||||||
MRI->clearKillFlags(I);
|
MRI->clearKillFlags(I);
|
||||||
RegsToClearKillFlags.clear();
|
RegsToClearKillFlags.clear();
|
||||||
|
|
||||||
|
reinsertSunkDebugDefs(MF);
|
||||||
|
SunkDebugDefs.clear();
|
||||||
|
|
||||||
return EverMadeChange;
|
return EverMadeChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -983,25 +1037,34 @@ bool MachineSinking::SinkInstruction(MachineInstr &MI, bool &SawStore,
|
|||||||
++InsertPos;
|
++InsertPos;
|
||||||
|
|
||||||
// Collect debug users of any vreg that this inst defines.
|
// Collect debug users of any vreg that this inst defines.
|
||||||
SmallVector<MachineInstr *, 4> DbgUsersToSink;
|
|
||||||
for (auto &MO : MI.operands()) {
|
for (auto &MO : MI.operands()) {
|
||||||
if (!MO.isReg() || !MO.isDef() || !MO.getReg().isVirtual())
|
if (!MO.isReg() || !MO.isDef() || !MO.getReg().isVirtual())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// If no DBG_VALUE uses this reg, skip further analysis.
|
||||||
if (!SeenDbgUsers.count(MO.getReg()))
|
if (!SeenDbgUsers.count(MO.getReg()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Sink any users that don't pass any other DBG_VALUEs for this variable.
|
SunkDebugDef &SunkDef = SunkDebugDefs[MO.getReg()];
|
||||||
|
SunkDef.MI = &MI;
|
||||||
|
|
||||||
|
// Record that these DBG_VALUEs refer to this sinking vreg, so that they
|
||||||
|
// can be re-specified after sinking completes. Try copy-propagating
|
||||||
|
// the original location too.
|
||||||
auto &Users = SeenDbgUsers[MO.getReg()];
|
auto &Users = SeenDbgUsers[MO.getReg()];
|
||||||
for (auto &User : Users) {
|
for (auto &User : Users) {
|
||||||
MachineInstr *DbgMI = User.getPointer();
|
MachineInstr *DbgMI = User.getPointer();
|
||||||
if (User.getInt()) {
|
DebugVariable Var(DbgMI->getDebugVariable(), DbgMI->getDebugExpression(),
|
||||||
// This DBG_VALUE would re-order assignments. If we can't copy-propagate
|
DbgMI->getDebugLoc()->getInlinedAt());
|
||||||
// it, it can't be recovered. Set it undef.
|
// If we can't copy-propagate the original DBG_VALUE, mark it undef, as
|
||||||
|
// its operand will not be available.
|
||||||
if (!attemptDebugCopyProp(MI, *DbgMI))
|
if (!attemptDebugCopyProp(MI, *DbgMI))
|
||||||
DbgMI->getOperand(0).setReg(0);
|
DbgMI->getOperand(0).setReg(0);
|
||||||
} else {
|
|
||||||
DbgUsersToSink.push_back(DbgMI);
|
// Debug users that don't pass any other DBG_VALUEs for this variable
|
||||||
}
|
// can be sunk.
|
||||||
|
if (!User.getInt())
|
||||||
|
SunkDef.InstsToSink.insert({Var, DbgMI});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1011,6 +1074,7 @@ bool MachineSinking::SinkInstruction(MachineInstr &MI, bool &SawStore,
|
|||||||
if (MI.getMF()->getFunction().getSubprogram() && MI.isCopy())
|
if (MI.getMF()->getFunction().getSubprogram() && MI.isCopy())
|
||||||
SalvageUnsunkDebugUsersOfCopy(MI, SuccToSinkTo);
|
SalvageUnsunkDebugUsersOfCopy(MI, SuccToSinkTo);
|
||||||
|
|
||||||
|
SmallVector<MachineInstr *, 4> DbgUsersToSink; // Deliberately empty
|
||||||
performSink(MI, *SuccToSinkTo, InsertPos, DbgUsersToSink);
|
performSink(MI, *SuccToSinkTo, InsertPos, DbgUsersToSink);
|
||||||
|
|
||||||
// Conservatively, clear any kill flags, since it's possible that they are no
|
// Conservatively, clear any kill flags, since it's possible that they are no
|
||||||
|
@ -9,6 +9,9 @@
|
|||||||
# Test two checks DBG_VALUEs do not sink past a DBG_VALUE in the same block
|
# Test two checks DBG_VALUEs do not sink past a DBG_VALUE in the same block
|
||||||
# that redefines the variable location, while three checks that copy-propagation
|
# that redefines the variable location, while three checks that copy-propagation
|
||||||
# happens in the same scenario.
|
# happens in the same scenario.
|
||||||
|
#
|
||||||
|
# Test four: if we sink a DBG_VALUE via an intermediate block, no spurious
|
||||||
|
# DBG_VALUE $noreg's should appear along the way.
|
||||||
--- |
|
--- |
|
||||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
target triple = "x86_64-unknown-linux-gnu"
|
target triple = "x86_64-unknown-linux-gnu"
|
||||||
@ -46,6 +49,22 @@
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define void @test4(i32* nocapture readonly %p) local_unnamed_addr !dbg !301 {
|
||||||
|
; Stripped
|
||||||
|
entry:
|
||||||
|
br label %block1
|
||||||
|
block1:
|
||||||
|
br label %block2
|
||||||
|
block2:
|
||||||
|
br label %exit
|
||||||
|
combined:
|
||||||
|
br label %branch1
|
||||||
|
branch1:
|
||||||
|
br label %exit
|
||||||
|
exit:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind readnone
|
; Function Attrs: nounwind readnone
|
||||||
declare void @llvm.dbg.value(metadata, i64, metadata, metadata)
|
declare void @llvm.dbg.value(metadata, i64, metadata, metadata)
|
||||||
|
|
||||||
@ -81,10 +100,15 @@
|
|||||||
!202 = !{!203}
|
!202 = !{!203}
|
||||||
!203 = !DILocalVariable(name: "r", arg: 1, scope: !201, file: !2, line: 2, type: !12)
|
!203 = !DILocalVariable(name: "r", arg: 1, scope: !201, file: !2, line: 2, type: !12)
|
||||||
!204 = !DILocation(line: 1, column: 1, scope: !201)
|
!204 = !DILocation(line: 1, column: 1, scope: !201)
|
||||||
|
!301 = distinct !DISubprogram(name: "test2", scope: !2, file: !2, line: 2, type: !10, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !1, retainedNodes: !302)
|
||||||
|
!302 = !{!303}
|
||||||
|
!303 = !DILocalVariable(name: "s", arg: 1, scope: !301, file: !2, line: 2, type: !12)
|
||||||
|
!304 = !DILocation(line: 1, column: 1, scope: !301)
|
||||||
|
|
||||||
; CHECK: [[VARNUM:![0-9]+]] = !DILocalVariable(name: "p",
|
; CHECK: [[VARNUM:![0-9]+]] = !DILocalVariable(name: "p",
|
||||||
; CHECK: [[VAR2NUM:![0-9]+]] = !DILocalVariable(name: "q",
|
; CHECK: [[VAR2NUM:![0-9]+]] = !DILocalVariable(name: "q",
|
||||||
; CHECK: [[VAR3NUM:![0-9]+]] = !DILocalVariable(name: "r",
|
; CHECK: [[VAR3NUM:![0-9]+]] = !DILocalVariable(name: "r",
|
||||||
|
; CHECK: [[VAR4NUM:![0-9]+]] = !DILocalVariable(name: "s",
|
||||||
|
|
||||||
...
|
...
|
||||||
---
|
---
|
||||||
@ -235,3 +259,85 @@ body: |
|
|||||||
$rax = MOV64rr %2
|
$rax = MOV64rr %2
|
||||||
RET 0
|
RET 0
|
||||||
...
|
...
|
||||||
|
---
|
||||||
|
name: test4
|
||||||
|
tracksRegLiveness: true
|
||||||
|
liveins:
|
||||||
|
- { reg: '$rdi', virtual-reg: '%2' }
|
||||||
|
- { reg: '$rsi', virtual-reg: '%2' }
|
||||||
|
body: |
|
||||||
|
bb.0.entry:
|
||||||
|
successors: %bb.1.block1, %bb.2.block2
|
||||||
|
liveins: $rdi, $esi
|
||||||
|
|
||||||
|
; The DBG_VALUE here should sink through several blocks, but not leave any
|
||||||
|
; additional DBG_VALUE $noregs on its way.
|
||||||
|
; CHECK-LABEL: bb.0.entry:
|
||||||
|
; CHECK: [[TEST4VREG:%[0-9]+]]:gr64 = COPY $rdi
|
||||||
|
; CHECK-NEXT: CMP32ri $esi, 0
|
||||||
|
; CHECK-NEXT: DBG_VALUE $noreg, $noreg, [[VAR4NUM]]
|
||||||
|
; CHECK-NEXT: JCC_1 %bb.1, 4
|
||||||
|
; CHECK-NEXT: JMP_1
|
||||||
|
|
||||||
|
%2:gr64 = COPY $rdi
|
||||||
|
%5:gr64 = ADD64ri8 %2, 1, implicit-def dead $eflags
|
||||||
|
CMP32ri $esi, 0, implicit-def $eflags
|
||||||
|
DBG_VALUE %5, $noreg, !303, !17, debug-location !304
|
||||||
|
JCC_1 %bb.1.block1, 4, implicit $eflags
|
||||||
|
JMP_1 %bb.2.block2
|
||||||
|
|
||||||
|
bb.1.block1:
|
||||||
|
successors: %bb.3.combined
|
||||||
|
liveins: $esi
|
||||||
|
|
||||||
|
; CHECK-LABEL: bb.1.block1
|
||||||
|
; CHECK-NOT: DBG_VALUE
|
||||||
|
; CHECK: JMP_1 %bb.3
|
||||||
|
|
||||||
|
JMP_1 %bb.3.combined
|
||||||
|
|
||||||
|
bb.2.block2:
|
||||||
|
successors: %bb.3.combined
|
||||||
|
liveins: $esi
|
||||||
|
|
||||||
|
; CHECK-LABEL: bb.2.block2
|
||||||
|
; CHECK-NOT: DBG_VALUE
|
||||||
|
; CHECK: JMP_1 %bb.3
|
||||||
|
|
||||||
|
JMP_1 %bb.3.combined
|
||||||
|
|
||||||
|
bb.3.combined:
|
||||||
|
successors: %bb.4.branch1, %bb.5.exit
|
||||||
|
liveins: $esi
|
||||||
|
|
||||||
|
; CHECK-LABEL: bb.3.combined
|
||||||
|
; CHECK-NOT: DBG_VALUE
|
||||||
|
; CHECK: JCC_1 %bb.4, 4, implicit $eflags
|
||||||
|
; CHECK-NEXT: JMP_1 %bb.5
|
||||||
|
|
||||||
|
CMP32ri $esi, 1, implicit-def $eflags
|
||||||
|
JCC_1 %bb.4.branch1, 4, implicit $eflags
|
||||||
|
JMP_1 %bb.5.exit
|
||||||
|
|
||||||
|
bb.4.branch1:
|
||||||
|
successors: %bb.5.exit
|
||||||
|
|
||||||
|
; This block should receive the sunk copy and DBG_VALUE.
|
||||||
|
; CHECK-LABEL: bb.4.branch1:
|
||||||
|
; CHECK-NOT: DBG_VALUE
|
||||||
|
; CHECK: [[TEST4VREG2:%[0-9]+]]:gr64 = ADD64ri8 [[TEST4VREG]], 1
|
||||||
|
; CHECK-NEXT: DBG_VALUE [[TEST4VREG2]], $noreg, [[VAR4NUM]]
|
||||||
|
; CHECK-NEXT: ADD64ri8
|
||||||
|
; CHECK: JMP_1 %bb.5
|
||||||
|
|
||||||
|
%1:gr64 = ADD64ri8 %5, 4, implicit-def dead $eflags
|
||||||
|
JMP_1 %bb.5.exit
|
||||||
|
|
||||||
|
bb.5.exit:
|
||||||
|
; CHECK-LABEL: bb.5.exit
|
||||||
|
; CHECK-NOT: DBG_VALUE
|
||||||
|
; CHECK: RET 0
|
||||||
|
|
||||||
|
$rax = MOV64rr %2
|
||||||
|
RET 0
|
||||||
|
...
|
||||||
|
Loading…
Reference in New Issue
Block a user