Skip to content

Commit d9085f6

Browse files
committed
[globalisel] Add lost debug locations verifier
Summary: This verifier tries to ensure that DebugLoc's don't just disappear as we transform the MIR. It observes the instructions created, erased, and changed and at checkpoints chosen by the client algorithm verifies the locations affected by those changes. In particular, it verifies that: * Every DebugLoc for an erased/changing instruction is still present on at least one new/changed instruction * Failing that, that there is a line-0 ___location in the new/changed instructions. It's not possible to confirm which locations were merged so it conservatively assumes all unaccounted for locations are accounted for by any line-0 ___location to avoid false positives. If that fails, it prints the lost locations in the debug output along with the instructions that should have accounted for them. In theory, this is usable by the legalizer, combiner, selector and any other pass that performs incremental changes to the MIR. However, it has so far only really been tested on the legalizer (not including the artifact combiner) where it has caught lots of lost locations, particularly in Custom legalizations. There's only one example here as my initial testing was on an out-of-tree target and I haven't done a pass over the in-tree targets yet. Depends on D77575, D77446 Reviewers: bogner, aprantl, vsk Subscribers: jvesely, nhaehnle, mgorny, rovka, hiraditya, volkan, kerbowa, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D77576
1 parent 7c6ca18 commit d9085f6

File tree

6 files changed

+237
-8
lines changed

6 files changed

+237
-8
lines changed

llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
namespace llvm {
2727

2828
class MachineRegisterInfo;
29+
class LostDebugLocObserver;
2930

3031
class Legalizer : public MachineFunctionPass {
3132
public:
@@ -71,6 +72,7 @@ class Legalizer : public MachineFunctionPass {
7172
static MFResult
7273
legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
7374
ArrayRef<GISelChangeObserver *> AuxObservers,
75+
LostDebugLocObserver &LocObserver,
7476
MachineIRBuilder &MIRBuilder);
7577
};
7678
} // End namespace llvm.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===----- llvm/CodeGen/GlobalISel/LostDebugLocObserver.h -------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
/// Tracks DebugLocs between checkpoints and verifies that they are transferred.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
#ifndef LLVM_CODEGEN_GLOBALISEL_LOSTDEBUGLOCOBSERVER_H
13+
#define LLVM_CODEGEN_GLOBALISEL_LOSTDEBUGLOCOBSERVER_H
14+
15+
#include "llvm/ADT/SmallSet.h"
16+
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
17+
18+
namespace llvm {
19+
class LostDebugLocObserver : public GISelChangeObserver {
20+
StringRef DebugType;
21+
SmallSet<DebugLoc, 4> LostDebugLocs;
22+
SmallPtrSet<MachineInstr *, 4> PotentialMIsForDebugLocs;
23+
unsigned NumLostDebugLocs = 0;
24+
25+
public:
26+
LostDebugLocObserver(StringRef DebugType) : DebugType(DebugType) {}
27+
28+
unsigned getNumLostDebugLocs() const { return NumLostDebugLocs; }
29+
30+
/// Call this to indicate that it's a good point to assess whether locations
31+
/// have been lost. Typically this will be when a logical change has been
32+
/// completed such as the caller has finished replacing some instructions with
33+
/// alternatives. When CheckDebugLocs is true, the locations will be checked
34+
/// to see if any have been lost since the last checkpoint. When
35+
/// CheckDebugLocs is false, it will just reset ready for the next checkpoint
36+
/// without checking anything. This can be helpful to limit the detection to
37+
/// easy-to-fix portions of an algorithm before allowing more difficult ones.
38+
void checkpoint(bool CheckDebugLocs = true);
39+
40+
void createdInstr(MachineInstr &MI) override;
41+
void erasingInstr(MachineInstr &MI) override;
42+
void changingInstr(MachineInstr &MI) override;
43+
void changedInstr(MachineInstr &MI) override;
44+
45+
private:
46+
void analyzeDebugLocations();
47+
};
48+
49+
} // namespace llvm
50+
#endif // ifndef LLVM_CODEGEN_GLOBALISEL_LOSTDEBUGLOCOBSERVER_H

llvm/lib/CodeGen/GlobalISel/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ add_llvm_component_library(LLVMGlobalISel
1616
LegalizerHelper.cpp
1717
LegalizerInfo.cpp
1818
Localizer.cpp
19+
LostDebugLocObserver.cpp
1920
MachineIRBuilder.cpp
2021
RegBankSelect.cpp
2122
RegisterBank.cpp

llvm/lib/CodeGen/GlobalISel/Legalizer.cpp

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "llvm/CodeGen/GlobalISel/GISelWorkList.h"
2222
#include "llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h"
2323
#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
24+
#include "llvm/CodeGen/GlobalISel/LostDebugLocObserver.h"
2425
#include "llvm/CodeGen/GlobalISel/Utils.h"
2526
#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
2627
#include "llvm/CodeGen/MachineRegisterInfo.h"
@@ -42,6 +43,27 @@ static cl::opt<bool>
4243
cl::desc("Should enable CSE in Legalizer"),
4344
cl::Optional, cl::init(false));
4445

46+
enum class DebugLocVerifyLevel {
47+
None,
48+
Legalizations,
49+
LegalizationsAndArtifactCombiners,
50+
};
51+
#ifndef NDEBUG
52+
static cl::opt<DebugLocVerifyLevel> VerifyDebugLocs(
53+
"verify-legalizer-debug-locs",
54+
cl::desc("Verify that debug locations are handled"),
55+
cl::values(clEnumVal(DebugLocVerifyLevel::None, "No verification"),
56+
clEnumVal(DebugLocVerifyLevel::Legalizations,
57+
"Verify legalizations"),
58+
clEnumVal(DebugLocVerifyLevel::LegalizationsAndArtifactCombiners,
59+
"Verify legalizations and artifact combines")),
60+
cl::init(DebugLocVerifyLevel::Legalizations));
61+
#else
62+
// Always disable it for release builds by preventing the observer from being
63+
// installed.
64+
static const DebugLocVerifyLevel VerifyDebugLocs = DebugLocVerifyLevel::None;
65+
#endif
66+
4567
char Legalizer::ID = 0;
4668
INITIALIZE_PASS_BEGIN(Legalizer, DEBUG_TYPE,
4769
"Legalize the Machine IR a function's Machine IR", false,
@@ -144,6 +166,7 @@ class LegalizerWorkListManager : public GISelChangeObserver {
144166
Legalizer::MFResult
145167
Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
146168
ArrayRef<GISelChangeObserver *> AuxObservers,
169+
LostDebugLocObserver &LocObserver,
147170
MachineIRBuilder &MIRBuilder) {
148171
MachineRegisterInfo &MRI = MF.getRegInfo();
149172

@@ -200,6 +223,7 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
200223
if (isTriviallyDead(MI, MRI)) {
201224
LLVM_DEBUG(dbgs() << MI << "Is dead; erasing.\n");
202225
MI.eraseFromParentAndMarkDBGValuesForRemoval();
226+
LocObserver.checkpoint();
203227
continue;
204228
}
205229

@@ -225,6 +249,7 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
225249
return {Changed, &MI};
226250
}
227251
WorkListObserver.printNewInstrs();
252+
LocObserver.checkpoint();
228253
Changed |= Res == LegalizerHelper::Legalized;
229254
}
230255
// Try to combine the instructions in RetryList again if there
@@ -239,6 +264,7 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
239264
return {Changed, RetryList.front()};
240265
}
241266
}
267+
LocObserver.checkpoint();
242268
while (!ArtifactList.empty()) {
243269
MachineInstr &MI = *ArtifactList.pop_back_val();
244270
assert(isPreISelGenericOpcode(MI.getOpcode()) &&
@@ -247,18 +273,23 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI,
247273
LLVM_DEBUG(dbgs() << MI << "Is dead\n");
248274
RemoveDeadInstFromLists(&MI);
249275
MI.eraseFromParentAndMarkDBGValuesForRemoval();
276+
LocObserver.checkpoint();
250277
continue;
251278
}
252279
SmallVector<MachineInstr *, 4> DeadInstructions;
253280
LLVM_DEBUG(dbgs() << "Trying to combine: " << MI);
254281
if (ArtCombiner.tryCombineInstruction(MI, DeadInstructions,
255282
WrapperObserver)) {
256283
WorkListObserver.printNewInstrs();
284+
LocObserver.checkpoint(
285+
VerifyDebugLocs ==
286+
DebugLocVerifyLevel::LegalizationsAndArtifactCombiners);
257287
for (auto *DeadMI : DeadInstructions) {
258288
LLVM_DEBUG(dbgs() << *DeadMI << "Is dead\n");
259289
RemoveDeadInstFromLists(DeadMI);
260290
DeadMI->eraseFromParentAndMarkDBGValuesForRemoval();
261291
}
292+
LocObserver.checkpoint();
262293
Changed = true;
263294
continue;
264295
}
@@ -307,9 +338,13 @@ bool Legalizer::runOnMachineFunction(MachineFunction &MF) {
307338
AuxObservers.push_back(CSEInfo);
308339
}
309340
assert(!CSEInfo || !errorToBool(CSEInfo->verify()));
341+
LostDebugLocObserver LocObserver(DEBUG_TYPE);
342+
if (VerifyDebugLocs > DebugLocVerifyLevel::None)
343+
AuxObservers.push_back(&LocObserver);
310344

311345
const LegalizerInfo &LI = *MF.getSubtarget().getLegalizerInfo();
312-
MFResult Result = legalizeMachineFunction(MF, LI, AuxObservers, *MIRBuilder);
346+
MFResult Result =
347+
legalizeMachineFunction(MF, LI, AuxObservers, LocObserver, *MIRBuilder);
313348

314349
if (Result.FailedOn) {
315350
reportGISelFailure(MF, TPC, MORE, "gisel-legalize",
@@ -326,6 +361,28 @@ bool Legalizer::runOnMachineFunction(MachineFunction &MF) {
326361
reportGISelFailure(MF, TPC, MORE, R);
327362
return false;
328363
}
364+
365+
if (LocObserver.getNumLostDebugLocs()) {
366+
MachineOptimizationRemarkMissed R("gisel-legalize", "LostDebugLoc",
367+
MF.getFunction().getSubprogram(),
368+
/*MBB=*/&*MF.begin());
369+
R << "lost "
370+
<< ore::NV("NumLostDebugLocs", LocObserver.getNumLostDebugLocs())
371+
<< " debug locations during pass";
372+
reportGISelWarning(MF, TPC, MORE, R);
373+
// Example remark:
374+
// --- !Missed
375+
// Pass: gisel-legalize
376+
// Name: GISelFailure
377+
// DebugLoc: { File: '.../legalize-urem.mir', Line: 1, Column: 0 }
378+
// Function: test_urem_s32
379+
// Args:
380+
// - String: 'lost '
381+
// - NumLostDebugLocs: '1'
382+
// - String: ' debug locations during pass'
383+
// ...
384+
}
385+
329386
// If for some reason CSE was not enabled, make sure that we invalidate the
330387
// CSEInfo object (as we currently declare that the analysis is preserved).
331388
// The next time get on the wrapper is called, it will force it to recompute
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//===----- llvm/CodeGen/GlobalISel/LostDebugLocObserver.cpp -----*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
/// Tracks DebugLocs between checkpoints and verifies that they are transferred.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/CodeGen/GlobalISel/LostDebugLocObserver.h"
14+
15+
using namespace llvm;
16+
17+
#define LOC_DEBUG(X) DEBUG_WITH_TYPE(DebugType.str().c_str(), X)
18+
19+
void LostDebugLocObserver::analyzeDebugLocations() {
20+
if (LostDebugLocs.empty()) {
21+
LOC_DEBUG(dbgs() << ".. No debug info was present\n");
22+
return;
23+
}
24+
if (PotentialMIsForDebugLocs.empty()) {
25+
LOC_DEBUG(
26+
dbgs() << ".. No instructions to carry debug info (dead code?)\n");
27+
return;
28+
}
29+
30+
LOC_DEBUG(dbgs() << ".. Searching " << PotentialMIsForDebugLocs.size()
31+
<< " instrs for " << LostDebugLocs.size() << " locations\n");
32+
SmallPtrSet<MachineInstr *, 4> FoundIn;
33+
for (MachineInstr *MI : PotentialMIsForDebugLocs) {
34+
if (!MI->getDebugLoc())
35+
continue;
36+
// Check this first in case there's a matching line-0 ___location on both input
37+
// and output.
38+
if (MI->getDebugLoc().getLine() == 0) {
39+
LOC_DEBUG(
40+
dbgs() << ".. Assuming line-0 ___location covers remainder (if any)\n");
41+
return;
42+
}
43+
if (LostDebugLocs.erase(MI->getDebugLoc())) {
44+
LOC_DEBUG(dbgs() << ".. .. found " << MI->getDebugLoc() << " in " << *MI);
45+
FoundIn.insert(MI);
46+
continue;
47+
}
48+
}
49+
if (LostDebugLocs.empty())
50+
return;
51+
52+
NumLostDebugLocs += LostDebugLocs.size();
53+
LOC_DEBUG({
54+
dbgs() << ".. Lost locations:\n";
55+
for (const DebugLoc &Loc : LostDebugLocs) {
56+
dbgs() << ".. .. ";
57+
Loc.print(dbgs());
58+
dbgs() << "\n";
59+
}
60+
dbgs() << ".. MIs with matched locations:\n";
61+
for (MachineInstr *MI : FoundIn)
62+
if (PotentialMIsForDebugLocs.erase(MI))
63+
dbgs() << ".. .. " << *MI;
64+
dbgs() << ".. Remaining MIs with unmatched/no locations:\n";
65+
for (const MachineInstr *MI : PotentialMIsForDebugLocs)
66+
dbgs() << ".. .. " << *MI;
67+
});
68+
}
69+
70+
void LostDebugLocObserver::checkpoint(bool CheckDebugLocs) {
71+
if (CheckDebugLocs)
72+
analyzeDebugLocations();
73+
PotentialMIsForDebugLocs.clear();
74+
LostDebugLocs.clear();
75+
}
76+
77+
void LostDebugLocObserver::createdInstr(MachineInstr &MI) {
78+
PotentialMIsForDebugLocs.insert(&MI);
79+
}
80+
81+
bool irTranslatorNeverAddsLocations(unsigned Opcode) {
82+
switch (Opcode) {
83+
default:
84+
return false;
85+
case TargetOpcode::G_CONSTANT:
86+
case TargetOpcode::G_FCONSTANT:
87+
case TargetOpcode::G_IMPLICIT_DEF:
88+
case TargetOpcode::G_GLOBAL_VALUE:
89+
return true;
90+
}
91+
}
92+
93+
void LostDebugLocObserver::erasingInstr(MachineInstr &MI) {
94+
if (irTranslatorNeverAddsLocations(MI.getOpcode()))
95+
return;
96+
97+
PotentialMIsForDebugLocs.erase(&MI);
98+
if (MI.getDebugLoc())
99+
LostDebugLocs.insert(MI.getDebugLoc());
100+
}
101+
102+
void LostDebugLocObserver::changingInstr(MachineInstr &MI) {
103+
if (irTranslatorNeverAddsLocations(MI.getOpcode()))
104+
return;
105+
106+
PotentialMIsForDebugLocs.erase(&MI);
107+
if (MI.getDebugLoc())
108+
LostDebugLocs.insert(MI.getDebugLoc());
109+
}
110+
111+
void LostDebugLocObserver::changedInstr(MachineInstr &MI) {
112+
PotentialMIsForDebugLocs.insert(&MI);
113+
}

llvm/unittests/CodeGen/GlobalISel/LegalizerTest.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#include "GISelMITest.h"
109
#include "llvm/CodeGen/GlobalISel/Legalizer.h"
10+
#include "GISelMITest.h"
11+
#include "llvm/CodeGen/GlobalISel/LostDebugLocObserver.h"
12+
13+
#define DEBUG_TYPE "legalizer-test"
1114

1215
using namespace LegalizeActions;
1316
using namespace LegalizeMutations;
@@ -60,9 +63,10 @@ TEST_F(AArch64GISelMITest, BasicLegalizerTest) {
6063
return;
6164

6265
ALegalizerInfo LI(MF->getSubtarget());
66+
LostDebugLocObserver LocObserver(DEBUG_TYPE);
6367

64-
Legalizer::MFResult Result =
65-
Legalizer::legalizeMachineFunction(*MF, LI, {}, B);
68+
Legalizer::MFResult Result = Legalizer::legalizeMachineFunction(
69+
*MF, LI, {&LocObserver}, LocObserver, B);
6670

6771
EXPECT_TRUE(isNullMIPtr(Result.FailedOn));
6872
EXPECT_TRUE(Result.Changed);
@@ -98,6 +102,7 @@ TEST_F(AArch64GISelMITest, UnorderedArtifactCombiningTest) {
98102
return;
99103

100104
ALegalizerInfo LI(MF->getSubtarget());
105+
LostDebugLocObserver LocObserver(DEBUG_TYPE);
101106

102107
// The events here unfold as follows:
103108
// 1. First, the function is scanned pre-forming the worklist of artifacts:
@@ -153,8 +158,8 @@ TEST_F(AArch64GISelMITest, UnorderedArtifactCombiningTest) {
153158
// pair(s) of artifacts that could be immediately combined out. After that
154159
// the process follows def-use chains, making them shorter at each step, thus
155160
// combining everything that can be combined in O(n) time.
156-
Legalizer::MFResult Result =
157-
Legalizer::legalizeMachineFunction(*MF, LI, {}, B);
161+
Legalizer::MFResult Result = Legalizer::legalizeMachineFunction(
162+
*MF, LI, {&LocObserver}, LocObserver, B);
158163

159164
EXPECT_TRUE(isNullMIPtr(Result.FailedOn));
160165
EXPECT_TRUE(Result.Changed);
@@ -191,9 +196,10 @@ TEST_F(AArch64GISelMITest, UnorderedArtifactCombiningManyCopiesTest) {
191196
return;
192197

193198
ALegalizerInfo LI(MF->getSubtarget());
199+
LostDebugLocObserver LocObserver(DEBUG_TYPE);
194200

195-
Legalizer::MFResult Result =
196-
Legalizer::legalizeMachineFunction(*MF, LI, {}, B);
201+
Legalizer::MFResult Result = Legalizer::legalizeMachineFunction(
202+
*MF, LI, {&LocObserver}, LocObserver, B);
197203

198204
EXPECT_TRUE(isNullMIPtr(Result.FailedOn));
199205
EXPECT_TRUE(Result.Changed);

0 commit comments

Comments
 (0)