@@ -45,61 +45,190 @@ cl::OptionCategory GICombinerOptionCategory(
45
45
);
46
46
} // end namespace llvm
47
47
48
- // / This class acts as the glue the joins the CombinerHelper to the overall
48
+ // / This class acts as the glue that joins the CombinerHelper to the overall
49
49
// / Combine algorithm. The CombinerHelper is intended to report the
50
50
// / modifications it makes to the MIR to the GISelChangeObserver and the
51
- // / observer subclass will act on these events. In this case, instruction
52
- // / erasure will cancel any future visits to the erased instruction and
53
- // / instruction creation will schedule that instruction for a future visit.
54
- // / Other Combiner implementations may require more complex behaviour from
55
- // / their GISelChangeObserver subclass.
51
+ // / observer subclass will act on these events.
56
52
class Combiner ::WorkListMaintainer : public GISelChangeObserver {
57
- using WorkListTy = GISelWorkList< 512 >;
58
- WorkListTy &WorkList;
53
+ protected:
54
+ # ifndef NDEBUG
59
55
// / The instructions that have been created but we want to report once they
60
56
// / have their operands. This is only maintained if debug output is requested.
61
- #ifndef NDEBUG
62
- SetVector<const MachineInstr *> CreatedInstrs;
57
+ SmallSetVector<const MachineInstr *, 32 > CreatedInstrs;
63
58
#endif
59
+ using Level = CombinerInfo::ObserverLevel;
64
60
65
61
public:
66
- WorkListMaintainer (WorkListTy &WorkList) : WorkList(WorkList) {}
62
+ static std::unique_ptr<WorkListMaintainer>
63
+ create (Level Lvl, WorkListTy &WorkList, MachineRegisterInfo &MRI);
64
+
67
65
virtual ~WorkListMaintainer () = default ;
68
66
67
+ void reportFullyCreatedInstrs () {
68
+ LLVM_DEBUG ({
69
+ for (auto *MI : CreatedInstrs) {
70
+ dbgs () << " Created: " << *MI;
71
+ }
72
+ CreatedInstrs.clear ();
73
+ });
74
+ }
75
+
76
+ virtual void reset () = 0;
77
+ virtual void appliedCombine () = 0;
78
+ };
79
+
80
+ // / A configurable WorkListMaintainer implementation.
81
+ // / The ObserverLevel determines how the WorkListMaintainer reacts to MIR
82
+ // / changes.
83
+ template <CombinerInfo::ObserverLevel Lvl>
84
+ class Combiner ::WorkListMaintainerImpl : public Combiner::WorkListMaintainer {
85
+ WorkListTy &WorkList;
86
+ MachineRegisterInfo &MRI;
87
+
88
+ // Defer handling these instructions until the combine finishes.
89
+ SmallSetVector<MachineInstr *, 32 > DeferList;
90
+
91
+ // Track VRegs that (might) have lost a use.
92
+ SmallSetVector<Register, 32 > LostUses;
93
+
94
+ public:
95
+ WorkListMaintainerImpl (WorkListTy &WorkList, MachineRegisterInfo &MRI)
96
+ : WorkList(WorkList), MRI(MRI) {}
97
+
98
+ virtual ~WorkListMaintainerImpl () = default ;
99
+
100
+ void reset () override {
101
+ DeferList.clear ();
102
+ LostUses.clear ();
103
+ }
104
+
69
105
void erasingInstr (MachineInstr &MI) override {
70
- LLVM_DEBUG (dbgs () << " Erasing: " << MI << " \n " );
106
+ // MI will become dangling, remove it from all lists.
107
+ LLVM_DEBUG (dbgs () << " Erasing: " << MI; CreatedInstrs.remove (&MI));
71
108
WorkList.remove (&MI);
109
+ if constexpr (Lvl != Level::Basic) {
110
+ DeferList.remove (&MI);
111
+ noteLostUses (MI);
112
+ }
72
113
}
114
+
73
115
void createdInstr (MachineInstr &MI) override {
74
- LLVM_DEBUG (dbgs () << " Creating: " << MI << " \n " );
75
- WorkList.insert (&MI);
76
- LLVM_DEBUG (CreatedInstrs.insert (&MI));
116
+ LLVM_DEBUG (dbgs () << " Creating: " << MI; CreatedInstrs.insert (&MI));
117
+ if constexpr (Lvl == Level::Basic)
118
+ WorkList.insert (&MI);
119
+ else
120
+ // Defer handling newly created instructions, because they don't have
121
+ // operands yet. We also insert them into the WorkList in reverse
122
+ // order so that they will be combined top down.
123
+ DeferList.insert (&MI);
77
124
}
125
+
78
126
void changingInstr (MachineInstr &MI) override {
79
- LLVM_DEBUG (dbgs () << " Changing: " << MI << " \n " );
80
- WorkList.insert (&MI);
127
+ LLVM_DEBUG (dbgs () << " Changing: " << MI);
128
+ // Some uses might get dropped when MI is changed.
129
+ // For now, overapproximate by assuming all uses will be dropped.
130
+ // TODO: Is a more precise heuristic or manual tracking of use count
131
+ // decrements worth it?
132
+ if constexpr (Lvl != Level::Basic)
133
+ noteLostUses (MI);
81
134
}
135
+
82
136
void changedInstr (MachineInstr &MI) override {
83
- LLVM_DEBUG (dbgs () << " Changed: " << MI << " \n " );
84
- WorkList.insert (&MI);
137
+ LLVM_DEBUG (dbgs () << " Changed: " << MI);
138
+ if constexpr (Lvl == Level::Basic)
139
+ WorkList.insert (&MI);
140
+ else
141
+ // Defer this for DCE
142
+ DeferList.insert (&MI);
85
143
}
86
144
87
- void reportFullyCreatedInstrs () {
88
- LLVM_DEBUG (for (const auto *MI
89
- : CreatedInstrs) {
90
- dbgs () << " Created: " ;
91
- MI->print (dbgs ());
92
- });
93
- LLVM_DEBUG (CreatedInstrs.clear ());
145
+ // Only track changes during the combine and then walk the def/use-chains once
146
+ // the combine is finished, because:
147
+ // - instructions might have multiple defs during the combine.
148
+ // - use counts aren't accurate during the combine.
149
+ void appliedCombine () override {
150
+ if constexpr (Lvl == Level::Basic)
151
+ return ;
152
+
153
+ // DCE deferred instructions and add them to the WorkList bottom up.
154
+ while (!DeferList.empty ()) {
155
+ MachineInstr &MI = *DeferList.pop_back_val ();
156
+ if (tryDCE (MI, MRI))
157
+ continue ;
158
+
159
+ if constexpr (Lvl >= Level::SinglePass)
160
+ addUsersToWorkList (MI);
161
+
162
+ WorkList.insert (&MI);
163
+ }
164
+
165
+ // Handle instructions that have lost a user.
166
+ while (!LostUses.empty ()) {
167
+ Register Use = LostUses.pop_back_val ();
168
+ MachineInstr *UseMI = MRI.getVRegDef (Use);
169
+ if (!UseMI)
170
+ continue ;
171
+
172
+ // If DCE succeeds, UseMI's uses are added back to LostUses by
173
+ // erasingInstr.
174
+ if (tryDCE (*UseMI, MRI))
175
+ continue ;
176
+
177
+ if constexpr (Lvl >= Level::SinglePass) {
178
+ // OneUse checks are relatively common, so we might be able to combine
179
+ // the single remaining user of this Reg.
180
+ if (MRI.hasOneNonDBGUser (Use))
181
+ WorkList.insert (&*MRI.use_instr_nodbg_begin (Use));
182
+
183
+ WorkList.insert (UseMI);
184
+ }
185
+ }
186
+ }
187
+
188
+ void noteLostUses (MachineInstr &MI) {
189
+ for (auto &Use : MI.explicit_uses ()) {
190
+ if (!Use.isReg () || !Use.getReg ().isVirtual ())
191
+ continue ;
192
+ LostUses.insert (Use.getReg ());
193
+ }
194
+ }
195
+
196
+ void addUsersToWorkList (MachineInstr &MI) {
197
+ for (auto &Def : MI.defs ()) {
198
+ Register DefReg = Def.getReg ();
199
+ if (!DefReg.isVirtual ())
200
+ continue ;
201
+ for (auto &UseMI : MRI.use_nodbg_instructions (DefReg)) {
202
+ WorkList.insert (&UseMI);
203
+ }
204
+ }
94
205
}
95
206
};
96
207
208
+ std::unique_ptr<Combiner::WorkListMaintainer>
209
+ Combiner::WorkListMaintainer::create (Level Lvl, WorkListTy &WorkList,
210
+ MachineRegisterInfo &MRI) {
211
+ switch (Lvl) {
212
+ case Level::Basic:
213
+ return std::make_unique<WorkListMaintainerImpl<Level::Basic>>(WorkList,
214
+ MRI);
215
+ case Level::DCE:
216
+ return std::make_unique<WorkListMaintainerImpl<Level::DCE>>(WorkList, MRI);
217
+ case Level::SinglePass:
218
+ return std::make_unique<WorkListMaintainerImpl<Level::SinglePass>>(WorkList,
219
+ MRI);
220
+ default :
221
+ llvm_unreachable (" Illegal ObserverLevel" );
222
+ }
223
+ }
224
+
97
225
Combiner::Combiner (MachineFunction &MF, CombinerInfo &CInfo,
98
226
const TargetPassConfig *TPC, GISelKnownBits *KB,
99
227
GISelCSEInfo *CSEInfo)
100
228
: Builder(CSEInfo ? std::make_unique<CSEMIRBuilder>()
101
229
: std::make_unique<MachineIRBuilder>()),
102
- WLObserver(std::make_unique<WorkListMaintainer>(WorkList)),
230
+ WLObserver(WorkListMaintainer::create(CInfo.ObserverLvl, WorkList,
231
+ MF.getRegInfo())),
103
232
ObserverWrapper(std::make_unique<GISelObserverWrapper>()), CInfo(CInfo),
104
233
Observer(*ObserverWrapper), B(*Builder), MF(MF), MRI(MF.getRegInfo()),
105
234
KB(KB), TPC(TPC), CSEInfo(CSEInfo) {
@@ -115,6 +244,15 @@ Combiner::Combiner(MachineFunction &MF, CombinerInfo &CInfo,
115
244
116
245
Combiner::~Combiner () = default ;
117
246
247
+ bool Combiner::tryDCE (MachineInstr &MI, MachineRegisterInfo &MRI) {
248
+ if (!isTriviallyDead (MI, MRI))
249
+ return false ;
250
+ LLVM_DEBUG (dbgs () << " Dead: " << MI);
251
+ llvm::salvageDebugInfo (MRI, MI);
252
+ MI.eraseFromParent ();
253
+ return true ;
254
+ }
255
+
118
256
bool Combiner::combineMachineInstrs () {
119
257
// If the ISel pipeline failed, do not bother running this pass.
120
258
// FIXME: Should this be here or in individual combiner passes.
@@ -141,27 +279,29 @@ bool Combiner::combineMachineInstrs() {
141
279
++Iteration;
142
280
LLVM_DEBUG (dbgs () << " \n\n Combiner iteration #" << Iteration << ' \n ' );
143
281
282
+ Changed = false ;
144
283
WorkList.clear ();
284
+ WLObserver->reset ();
145
285
ObserverWrapper->clearObservers ();
146
286
if (CSEInfo)
147
287
ObserverWrapper->addObserver (CSEInfo);
148
288
289
+ // If Observer-based DCE is enabled, perform full DCE only before the first
290
+ // iteration.
291
+ bool EnableDCE = CInfo.ObserverLvl >= CombinerInfo::ObserverLevel::DCE
292
+ ? CInfo.EnableFullDCE && Iteration == 1
293
+ : CInfo.EnableFullDCE ;
294
+
149
295
// Collect all instructions. Do a post order traversal for basic blocks and
150
296
// insert with list bottom up, so while we pop_back_val, we'll traverse top
151
297
// down RPOT.
152
- Changed = false ;
153
-
154
298
RAIIMFObsDelInstaller DelInstall (MF, *ObserverWrapper);
155
299
for (MachineBasicBlock *MBB : post_order (&MF)) {
156
300
for (MachineInstr &CurMI :
157
301
llvm::make_early_inc_range (llvm::reverse (*MBB))) {
158
302
// Erase dead insts before even adding to the list.
159
- if (isTriviallyDead (CurMI, MRI)) {
160
- LLVM_DEBUG (dbgs () << CurMI << " Is dead; erasing.\n " );
161
- llvm::salvageDebugInfo (MRI, CurMI);
162
- CurMI.eraseFromParent ();
303
+ if (EnableDCE && tryDCE (CurMI, MRI))
163
304
continue ;
164
- }
165
305
WorkList.deferred_insert (&CurMI);
166
306
}
167
307
}
@@ -171,10 +311,13 @@ bool Combiner::combineMachineInstrs() {
171
311
ObserverWrapper->addObserver (WLObserver.get ());
172
312
// Main Loop. Process the instructions here.
173
313
while (!WorkList.empty ()) {
174
- MachineInstr *CurrInst = WorkList.pop_back_val ();
175
- LLVM_DEBUG (dbgs () << " \n Try combining " << *CurrInst;);
176
- Changed |= tryCombineAll (*CurrInst);
177
- WLObserver->reportFullyCreatedInstrs ();
314
+ MachineInstr &CurrInst = *WorkList.pop_back_val ();
315
+ LLVM_DEBUG (dbgs () << " \n Try combining " << CurrInst);
316
+ bool AppliedCombine = tryCombineAll (CurrInst);
317
+ LLVM_DEBUG (WLObserver->reportFullyCreatedInstrs ());
318
+ Changed |= AppliedCombine;
319
+ if (AppliedCombine)
320
+ WLObserver->appliedCombine ();
178
321
}
179
322
MFChanged |= Changed;
180
323
0 commit comments