Skip to content

Commit eabd2a9

Browse files
committed
[DebugInfo][RemoveDIs] Add a DPValue implementation for instcombine sinking
In instcombine, when we sink an instruction into a successor block, we try to clone and salvage all the variable assignments that use that Value. This is a behaviour that's (IMO) flawed, but there are important use cases where we want to avoid regressions, thus we're implementing this for the non-instruction debug-info representation. This patch refactors the dbg.value sinking code into it's own function, and installs a parallel implementation for DPValues, the non-instruction debug-info container. This is mostly identical to the dbg.value implementation, except that we don't have an easy-to-access ordering between DPValues, and have to jump through extra hoops to establish one in the (rare) cases where that ordering is required. The test added represents a common use-case in LLVM where these behaviours are important: a loop has been completely optimised away, leaving several dbg.values in a row referring to an instruction that's going to sink. The dbg.values should sink in both dbg.value and RemoveDIs mode, and additionally only the last assignment should sink.
1 parent 9052512 commit eabd2a9

File tree

5 files changed

+231
-7
lines changed

5 files changed

+231
-7
lines changed

llvm/include/llvm/IR/DebugProgramInstruction.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class DPValue : public ilist_node<DPValue>, private DebugValueUser {
8585
public:
8686
void deleteInstr();
8787

88+
const Instruction *getInstruction() const;
8889
const BasicBlock *getParent() const;
8990
BasicBlock *getParent();
9091
void dump() const;

llvm/lib/IR/DebugProgramInstruction.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ void DPValue::handleChangedLocation(Metadata *NewLocation) {
225225
resetDebugValue(NewLocation);
226226
}
227227

228+
const Instruction *DPValue::getInstruction() const {
229+
return Marker->MarkedInstr;
230+
}
231+
228232
const BasicBlock *DPValue::getParent() const {
229233
return Marker->MarkedInstr->getParent();
230234
}

llvm/lib/Transforms/InstCombine/InstCombineInternal.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,13 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
732732
Value *EvaluateInDifferentType(Value *V, Type *Ty, bool isSigned);
733733

734734
bool tryToSinkInstruction(Instruction *I, BasicBlock *DestBlock);
735+
void tryToSinkInstructionDbgValues(
736+
Instruction *I, BasicBlock::iterator InsertPos, BasicBlock *SrcBlock,
737+
BasicBlock *DestBlock, SmallVectorImpl<DbgVariableIntrinsic *> &DbgUsers);
738+
void tryToSinkInstructionDPValues(Instruction *I,
739+
BasicBlock::iterator InsertPos,
740+
BasicBlock *SrcBlock, BasicBlock *DestBlock,
741+
SmallVectorImpl<DPValue *> &DPUsers);
735742

736743
bool removeInstructionsBeforeUnreachable(Instruction &I);
737744
void addDeadEdge(BasicBlock *From, BasicBlock *To,

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 145 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4217,12 +4217,29 @@ bool InstCombinerImpl::tryToSinkInstruction(Instruction *I,
42174217
// mark the location undef: we know it was supposed to receive a new location
42184218
// here, but that computation has been sunk.
42194219
SmallVector<DbgVariableIntrinsic *, 2> DbgUsers;
4220-
findDbgUsers(DbgUsers, I);
4220+
SmallVector<DPValue *, 2> DPValues;
4221+
findDbgUsers(DbgUsers, I, &DPValues);
4222+
tryToSinkInstructionDbgValues(I, InsertPos, SrcBlock, DestBlock, DbgUsers);
4223+
tryToSinkInstructionDPValues(I, InsertPos, SrcBlock, DestBlock, DPValues);
4224+
4225+
// PS: there are numerous flaws with this behaviour, not least that right now
4226+
// assignments can be re-ordered past other assignments to the same variable
4227+
// if they use different Values. Creating more undef assignements can never be
4228+
// undone. And salvaging all users outside of this block can un-necessarily
4229+
// alter the lifetime of the live-value that the variable refers to.
4230+
// Some of these things can be resolved by tolerating debug use-before-defs in
4231+
// LLVM-IR, however it depends on the instruction-referencing CodeGen backend
4232+
// being used for more architectures.
42214233

4234+
return true;
4235+
}
4236+
4237+
void InstCombinerImpl::tryToSinkInstructionDbgValues(
4238+
Instruction *I, BasicBlock::iterator InsertPos, BasicBlock *SrcBlock,
4239+
BasicBlock *DestBlock, SmallVectorImpl<DbgVariableIntrinsic *> &DbgUsers) {
42224240
// For all debug values in the destination block, the sunk instruction
42234241
// will still be available, so they do not need to be dropped.
42244242
SmallVector<DbgVariableIntrinsic *, 2> DbgUsersToSalvage;
4225-
SmallVector<DPValue *, 2> DPValuesToSalvage;
42264243
for (auto &DbgUser : DbgUsers)
42274244
if (DbgUser->getParent() != DestBlock)
42284245
DbgUsersToSalvage.push_back(DbgUser);
@@ -4266,19 +4283,140 @@ bool InstCombinerImpl::tryToSinkInstruction(Instruction *I,
42664283

42674284
// Perform salvaging without the clones, then sink the clones.
42684285
if (!DIIClones.empty()) {
4269-
// RemoveDIs: pass in empty vector of DPValues until we get to instrumenting
4270-
// this pass.
4271-
SmallVector<DPValue *, 1> DummyDPValues;
4272-
salvageDebugInfoForDbgValues(*I, DbgUsersToSalvage, DummyDPValues);
4286+
salvageDebugInfoForDbgValues(*I, DbgUsersToSalvage, {});
42734287
// The clones are in reverse order of original appearance, reverse again to
42744288
// maintain the original order.
42754289
for (auto &DIIClone : llvm::reverse(DIIClones)) {
42764290
DIIClone->insertBefore(&*InsertPos);
42774291
LLVM_DEBUG(dbgs() << "SINK: " << *DIIClone << '\n');
42784292
}
42794293
}
4294+
}
42804295

4281-
return true;
4296+
void InstCombinerImpl::tryToSinkInstructionDPValues(
4297+
Instruction *I, BasicBlock::iterator InsertPos, BasicBlock *SrcBlock,
4298+
BasicBlock *DestBlock, SmallVectorImpl<DPValue *> &DPValues) {
4299+
// Implementation of tryToSinkInstructionDbgValues, but for the DPValue of
4300+
// variable assignments rather than dbg.values.
4301+
4302+
// Fetch all DPValues not already in the destination.
4303+
SmallVector<DPValue *, 2> DPValuesToSalvage;
4304+
for (auto &DPV : DPValues)
4305+
if (DPV->getParent() != DestBlock)
4306+
DPValuesToSalvage.push_back(DPV);
4307+
4308+
// Fetch a second collection, of DPValues in the source block that we're going
4309+
// to sink.
4310+
SmallVector<DPValue *> DPValuesToSink;
4311+
for (DPValue *DPV : DPValuesToSalvage)
4312+
if (DPV->getParent() == SrcBlock)
4313+
DPValuesToSink.push_back(DPV);
4314+
4315+
// Sort DPValues according to their position in the block. This is a partial
4316+
// order: DPValues attached to different instructions will be ordered by the
4317+
// instruction order, but DPValues attached to the same instruction won't
4318+
// have an order.
4319+
auto Order = [](DPValue *A, DPValue *B) -> bool {
4320+
return B->getInstruction()->comesBefore(A->getInstruction());
4321+
};
4322+
llvm::stable_sort(DPValuesToSink, Order);
4323+
4324+
// If there are two assignments to the same variable attached to the same
4325+
// instruction, the ordering between the two assignments is important. Scan
4326+
// for this (rare) case and establish which is the last assignment.
4327+
using InstVarPair = std::pair<const Instruction *, DebugVariable>;
4328+
SmallDenseMap<InstVarPair, DPValue *> FilterOutMap;
4329+
if (DPValuesToSink.size() > 1) {
4330+
SmallDenseMap<InstVarPair, unsigned> CountMap;
4331+
// Count how many assignments to each variable there is per instruction.
4332+
for (DPValue *DPV : DPValuesToSink) {
4333+
DebugVariable DbgUserVariable =
4334+
DebugVariable(DPV->getVariable(), DPV->getExpression(),
4335+
DPV->getDebugLoc()->getInlinedAt());
4336+
CountMap[std::make_pair(DPV->getInstruction(), DbgUserVariable)] += 1;
4337+
}
4338+
4339+
// If there are any instructions with two assignments, add them to the
4340+
// FilterOutMap to record that they need extra filtering.
4341+
SmallPtrSet<const Instruction *, 4> DupSet;
4342+
for (auto It : CountMap) {
4343+
if (It.second > 1) {
4344+
FilterOutMap[It.first] = nullptr;
4345+
DupSet.insert(It.first.first);
4346+
}
4347+
}
4348+
4349+
// For all instruction/variable pairs needing extra filtering, find the
4350+
// latest assignment.
4351+
for (const Instruction *Inst : DupSet) {
4352+
for (DPValue &DPV : llvm::reverse(Inst->getDbgValueRange())) {
4353+
DebugVariable DbgUserVariable =
4354+
DebugVariable(DPV.getVariable(), DPV.getExpression(),
4355+
DPV.getDebugLoc()->getInlinedAt());
4356+
auto FilterIt =
4357+
FilterOutMap.find(std::make_pair(Inst, DbgUserVariable));
4358+
if (FilterIt == FilterOutMap.end())
4359+
continue;
4360+
if (FilterIt->second != nullptr)
4361+
continue;
4362+
FilterIt->second = &DPV;
4363+
}
4364+
}
4365+
}
4366+
4367+
// Perform cloning of the DPValues that we plan on sinking, filter out any
4368+
// duplicate assignments identified above.
4369+
SmallVector<DPValue *, 2> DPVClones;
4370+
SmallSet<DebugVariable, 4> SunkVariables;
4371+
for (DPValue *DPV : DPValuesToSink) {
4372+
if (DPV->Type == DPValue::LocationType::Declare)
4373+
continue;
4374+
4375+
DebugVariable DbgUserVariable =
4376+
DebugVariable(DPV->getVariable(), DPV->getExpression(),
4377+
DPV->getDebugLoc()->getInlinedAt());
4378+
4379+
// For any variable where there were multiple assignments in the same place,
4380+
// ignore all but the last assignment.
4381+
if (!FilterOutMap.empty()) {
4382+
InstVarPair IVP = std::make_pair(DPV->getInstruction(), DbgUserVariable);
4383+
auto It = FilterOutMap.find(IVP);
4384+
4385+
// Filter out.
4386+
if (It != FilterOutMap.end() && It->second != DPV)
4387+
continue;
4388+
}
4389+
4390+
if (!SunkVariables.insert(DbgUserVariable).second)
4391+
continue;
4392+
4393+
// FIXME: dbg.assign equivalence. dbg.assigns here enter the SunkVariables
4394+
// map, but don't actually get sunk.
4395+
4396+
DPVClones.emplace_back(DPV->clone());
4397+
LLVM_DEBUG(dbgs() << "CLONE: " << *DPVClones.back() << '\n');
4398+
}
4399+
4400+
// Perform salvaging without the clones, then sink the clones.
4401+
if (DPVClones.empty())
4402+
return;
4403+
4404+
salvageDebugInfoForDbgValues(*I, {}, DPValuesToSalvage);
4405+
4406+
// The clones are in reverse order of original appearance. Assert that the
4407+
// head bit is set on the iterator as we _should_ have received it via
4408+
// getFirstInsertionPt. Inserting like this will reverse the clone order as
4409+
// we'll repeatedly insert at the head, such as:
4410+
// DPV-3 (third insertion goes here)
4411+
// DPV-2 (second insertion goes here)
4412+
// DPV-1 (first insertion goes here)
4413+
// Any-Prior-DPVs
4414+
// InsertPtInst
4415+
assert(InsertPos.getHeadBit());
4416+
for (DPValue *DPVClone : DPVClones) {
4417+
InsertPos->getParent()->insertDPValueBefore(DPVClone, InsertPos);
4418+
LLVM_DEBUG(dbgs() << "SINK: " << *DPVClone << '\n');
4419+
}
42824420
}
42834421

42844422
bool InstCombinerImpl::run() {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
; RUN: opt %s -o - -S --passes=instcombine | FileCheck %s
2+
; RUN: opt %s -o - -S --passes=instcombine --try-experimental-debuginfo-iterators | FileCheck %s
3+
;
4+
; CHECK-LABEL: for.body:
5+
; CHECK-NEXT: %sub.ptr.rhs.cast.i.i = ptrtoint ptr %call2.i.i to i64,
6+
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i64 %sub.ptr.rhs.cast.i.i, metadata !{{[0-9]*}}, metadata !DIExpression(DW_OP_LLVM_convert, 64, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_constu, 1, DW_OP_minus, DW_OP_stack_value)
7+
;
8+
;; The code below is representative of a common situation: where we've had a
9+
;; loop be completely optimised out, leaving dbg.values representing the
10+
;; assignments for it, including a rotated assignment to the loop counter.
11+
;; Below, it's the two dbg.values in the entry block, assigning first the
12+
;; value of %conv.i, then the value of %conv.i minus one.
13+
;;
14+
;; When instcombine sinks %conv.i, it's critical that if it sinks a dbg.value
15+
;; with it, it sinks the most recent assignment. Otherwise it will re-order the
16+
;; assignments below, and a fall counter assignment will continue on from the
17+
;; end of the optimised-out loop.
18+
;;
19+
;; The check lines test that when the trunc sinks (along with the ptrtoint),
20+
;; we get the dbg.value with a DW_OP_minus in it.
21+
22+
; ModuleID = 'tmp.ll'
23+
source_filename = "tmp.ll"
24+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
25+
target triple = "x86_64-unknown-linux-gnu"
26+
27+
declare void @llvm.dbg.value(metadata, metadata, metadata)
28+
declare void @llvm.dbg.declare(metadata, metadata, metadata)
29+
30+
define void @_ZN4llvm16MCObjectStreamer15EmitInstructionERKNS_6MCInstE(i1 %tobool.not) local_unnamed_addr {
31+
entry:
32+
%call2.i.i = load volatile ptr, ptr null, align 8, !dbg !4
33+
%sub.ptr.rhs.cast.i.i = ptrtoint ptr %call2.i.i to i64, !dbg !4
34+
%conv.i = trunc i64 %sub.ptr.rhs.cast.i.i to i32, !dbg !4
35+
tail call void @llvm.dbg.value(metadata i32 %conv.i, metadata !16, metadata !DIExpression()), !dbg !18
36+
tail call void @llvm.dbg.value(metadata i32 %conv.i, metadata !16, metadata !DIExpression(DW_OP_constu, 1, DW_OP_minus, DW_OP_stack_value)), !dbg !18
37+
br i1 %tobool.not, label %common.ret, label %for.body
38+
39+
common.ret: ; preds = %for.body, %entry
40+
ret void
41+
42+
for.body: ; preds = %entry
43+
%call2 = call ptr @_ZNK4llvm6MCInst10getOperandEj(i32 %conv.i)
44+
br label %common.ret
45+
}
46+
47+
declare i32 @_ZNK4llvm6MCInst14getNumOperandsEv()
48+
49+
declare ptr @_ZNK4llvm6MCInst10getOperandEj(i32) local_unnamed_addr
50+
51+
declare i64 @_ZNK4llvm25SmallVectorTemplateCommonINS_9MCOperandEvE4sizeEv()
52+
53+
!llvm.dbg.cu = !{!0}
54+
!llvm.module.flags = !{!3}
55+
56+
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !2, globals: !2, imports: !2, splitDebugInlining: false, nameTableKind: None)
57+
!1 = !DIFile(filename: "/fast/fs/llvm34/lib/MC/MCObjectStreamer.cpp", directory: "/fast/fs/build34llvmstage", checksumkind: CSK_MD5, checksum: "43f3adff5ece50116e446307bd92824d")
58+
!2 = !{}
59+
!3 = !{i32 2, !"Debug Info Version", i32 3}
60+
!4 = !DILocation(line: 197, column: 26, scope: !5)
61+
!5 = distinct !DILexicalBlock(scope: !7, file: !6, line: 197, column: 3)
62+
!6 = !DIFile(filename: "llvm34/lib/MC/MCObjectStreamer.cpp", directory: "/fast/fs", checksumkind: CSK_MD5, checksum: "43f3adff5ece50116e446307bd92824d")
63+
!7 = distinct !DISubprogram(name: "EmitInstruction", linkageName: "_ZN4llvm16MCObjectStreamer15EmitInstructionERKNS_6MCInstE", scope: !8, file: !6, line: 195, type: !13, scopeLine: 195, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !15, retainedNodes: !2)
64+
!8 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "MCObjectStreamer", scope: !10, file: !9, line: 33, size: 2432, flags: DIFlagTypePassByReference | DIFlagNonTrivial, elements: !2, vtableHolder: !11)
65+
!9 = !DIFile(filename: "llvm34/include/llvm/MC/MCObjectStreamer.h", directory: "/fast/fs", checksumkind: CSK_MD5, checksum: "364947c58883b0a72d98313c0775422d")
66+
!10 = !DINamespace(name: "llvm", scope: null)
67+
!11 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "MCStreamer", scope: !10, file: !12, line: 108, size: 2240, flags: DIFlagFwdDecl | DIFlagNonTrivial, identifier: "_ZTSN4llvm10MCStreamerE")
68+
!12 = !DIFile(filename: "llvm34/include/llvm/MC/MCStreamer.h", directory: "/fast/fs", checksumkind: CSK_MD5, checksum: "1fdd4f3a9a6a2340c2ba553eefe0e90b")
69+
!13 = distinct !DISubroutineType(types: !14)
70+
!14 = !{null}
71+
!15 = !DISubprogram(name: "EmitInstruction", linkageName: "_ZN4llvm16MCObjectStreamer15EmitInstructionERKNS_6MCInstE", scope: !8, file: !9, line: 88, type: !13, scopeLine: 88, containingType: !8, virtualIndex: 86, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagVirtual | DISPFlagOptimized)
72+
!16 = !DILocalVariable(name: "i", scope: !5, file: !6, line: 197, type: !17)
73+
!17 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
74+
!18 = !DILocation(line: 0, scope: !5)

0 commit comments

Comments
 (0)