Skip to content

Commit 3331185

Browse files
[RFC][GlobalISel] InstructionSelect: Allow arbitrary instruction erasure
1 parent a8e1c3e commit 3331185

File tree

2 files changed

+125
-82
lines changed

2 files changed

+125
-82
lines changed

llvm/include/llvm/CodeGen/GlobalISel/InstructionSelect.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
namespace llvm {
2222

23+
class InstructionSelector;
2324
class BlockFrequencyInfo;
2425
class ProfileSummaryInfo;
2526

@@ -55,10 +56,15 @@ class InstructionSelect : public MachineFunctionPass {
5556
bool runOnMachineFunction(MachineFunction &MF) override;
5657

5758
protected:
59+
class MIIteratorMaintainer;
60+
61+
InstructionSelector *ISel = nullptr;
5862
BlockFrequencyInfo *BFI = nullptr;
5963
ProfileSummaryInfo *PSI = nullptr;
6064

6165
CodeGenOptLevel OptLevel = CodeGenOptLevel::None;
66+
67+
bool select(MachineInstr &MI);
6268
};
6369
} // End namespace llvm.
6470

llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp

Lines changed: 119 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
#include "llvm/CodeGen/GlobalISel/InstructionSelect.h"
1313
#include "llvm/ADT/PostOrderIterator.h"
1414
#include "llvm/ADT/ScopeExit.h"
15+
#include "llvm/ADT/SetVector.h"
1516
#include "llvm/Analysis/LazyBlockFrequencyInfo.h"
1617
#include "llvm/Analysis/ProfileSummaryInfo.h"
18+
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
1719
#include "llvm/CodeGen/GlobalISel/GISelKnownBits.h"
1820
#include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
1921
#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
@@ -62,6 +64,52 @@ INITIALIZE_PASS_END(InstructionSelect, DEBUG_TYPE,
6264
"Select target instructions out of generic instructions",
6365
false, false)
6466

67+
/// This class observes instruction insertions/removals.
68+
/// InstructionSelect stores an iterator of the instruction prior to the one
69+
/// that is currently being selected to determine which instruction to select
70+
/// next. Previously this meant that selecting multiple instructions at once was
71+
/// illegal behavior due to potential invalidation of this iterator. This is
72+
/// a non-obvious limitation for selector implementers. Therefore, to allow
73+
/// deletion of arbitrary instructions, we detect this case and continue
74+
/// selection with the predecessor of the deleted instruction.
75+
class InstructionSelect::MIIteratorMaintainer
76+
: public MachineFunction::Delegate {
77+
#ifndef NDEBUG
78+
SetVector<const MachineInstr *> CreatedInstrs;
79+
#endif
80+
public:
81+
MachineBasicBlock::reverse_iterator MII;
82+
83+
void MF_HandleInsertion(MachineInstr &MI) override {
84+
LLVM_DEBUG(dbgs() << "Creating: " << MI; CreatedInstrs.insert(&MI));
85+
}
86+
87+
void MF_HandleRemoval(MachineInstr &MI) override {
88+
LLVM_DEBUG(dbgs() << "Erasing: " << MI; CreatedInstrs.remove(&MI));
89+
if (MII.getInstrIterator().getNodePtr() == &MI) {
90+
// If the iterator points to the MI that will be erased (i.e. the MI prior
91+
// to the MI that is currently being selected), the iterator would be
92+
// invalidated. Continue selection with its predecessor.
93+
++MII;
94+
LLVM_DEBUG(dbgs() << "Instruction removal updated iterator.\n");
95+
}
96+
}
97+
98+
void reportFullyCreatedInstrs() {
99+
LLVM_DEBUG({
100+
if (CreatedInstrs.empty()) {
101+
dbgs() << "Created no instructions.\n";
102+
} else {
103+
dbgs() << "Created:\n";
104+
for (const auto *MI : CreatedInstrs) {
105+
dbgs() << " " << *MI;
106+
}
107+
CreatedInstrs.clear();
108+
}
109+
});
110+
}
111+
};
112+
65113
InstructionSelect::InstructionSelect(CodeGenOptLevel OL)
66114
: MachineFunctionPass(ID), OptLevel(OL) {}
67115

@@ -93,7 +141,7 @@ bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
93141
LLVM_DEBUG(dbgs() << "Selecting function: " << MF.getName() << '\n');
94142

95143
const TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
96-
InstructionSelector *ISel = MF.getSubtarget().getInstructionSelector();
144+
ISel = MF.getSubtarget().getInstructionSelector();
97145
ISel->setTargetPassConfig(&TPC);
98146

99147
CodeGenOptLevel OldOptLevel = OptLevel;
@@ -137,80 +185,36 @@ bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
137185
// Keep track of selected blocks, so we can delete unreachable ones later.
138186
DenseSet<MachineBasicBlock *> SelectedBlocks;
139187

140-
for (MachineBasicBlock *MBB : post_order(&MF)) {
141-
ISel->CurMBB = MBB;
142-
SelectedBlocks.insert(MBB);
143-
if (MBB->empty())
144-
continue;
145-
146-
// Select instructions in reverse block order. We permit erasing so have
147-
// to resort to manually iterating and recognizing the begin (rend) case.
148-
bool ReachedBegin = false;
149-
for (auto MII = std::prev(MBB->end()), Begin = MBB->begin();
150-
!ReachedBegin;) {
151-
#ifndef NDEBUG
152-
// Keep track of the insertion range for debug printing.
153-
const auto AfterIt = std::next(MII);
154-
#endif
155-
// Select this instruction.
156-
MachineInstr &MI = *MII;
157-
158-
// And have our iterator point to the next instruction, if there is one.
159-
if (MII == Begin)
160-
ReachedBegin = true;
161-
else
162-
--MII;
163-
164-
LLVM_DEBUG(dbgs() << "Selecting: \n " << MI);
165-
166-
// We could have folded this instruction away already, making it dead.
167-
// If so, erase it.
168-
if (isTriviallyDead(MI, MRI)) {
169-
LLVM_DEBUG(dbgs() << "Is dead; erasing.\n");
170-
salvageDebugInfo(MRI, MI);
171-
MI.eraseFromParent();
172-
continue;
173-
}
174-
175-
// Eliminate hints or G_CONSTANT_FOLD_BARRIER.
176-
if (isPreISelGenericOptimizationHint(MI.getOpcode()) ||
177-
MI.getOpcode() == TargetOpcode::G_CONSTANT_FOLD_BARRIER) {
178-
auto [DstReg, SrcReg] = MI.getFirst2Regs();
179-
180-
// At this point, the destination register class of the op may have
181-
// been decided.
182-
//
183-
// Propagate that through to the source register.
184-
const TargetRegisterClass *DstRC = MRI.getRegClassOrNull(DstReg);
185-
if (DstRC)
186-
MRI.setRegClass(SrcReg, DstRC);
187-
assert(canReplaceReg(DstReg, SrcReg, MRI) &&
188-
"Must be able to replace dst with src!");
189-
MI.eraseFromParent();
190-
MRI.replaceRegWith(DstReg, SrcReg);
191-
continue;
192-
}
193-
194-
if (MI.getOpcode() == TargetOpcode::G_INVOKE_REGION_START) {
195-
MI.eraseFromParent();
196-
continue;
197-
}
198-
199-
if (!ISel->select(MI)) {
200-
// FIXME: It would be nice to dump all inserted instructions. It's
201-
// not obvious how, esp. considering select() can insert after MI.
202-
reportGISelFailure(MF, TPC, MORE, "gisel-select", "cannot select", MI);
203-
return false;
188+
{
189+
// Observe IR insertions and removals during selection.
190+
// We only install a MachineFunction::Delegate instead of a
191+
// GISelChangeObserver, because we do not want notifications about changed
192+
// instructions. This prevents significant compile-time regressions from
193+
// e.g. constrainOperandRegClass().
194+
MIIteratorMaintainer MIIMaintainer;
195+
RAIIDelegateInstaller DelInstaller(MF, &MIIMaintainer);
196+
197+
for (MachineBasicBlock *MBB : post_order(&MF)) {
198+
ISel->CurMBB = MBB;
199+
SelectedBlocks.insert(MBB);
200+
201+
// Select instructions in reverse block order.
202+
MIIMaintainer.MII = MBB->rbegin();
203+
for (auto End = MBB->rend(); MIIMaintainer.MII != End;) {
204+
MachineInstr &MI = *MIIMaintainer.MII;
205+
// Increment early to skip instructions inserted by select().
206+
++MIIMaintainer.MII;
207+
208+
LLVM_DEBUG(dbgs() << "\nSelect: " << MI);
209+
if (!select(MI)) {
210+
LLVM_DEBUG(dbgs() << "Selection failed!\n";
211+
MIIMaintainer.reportFullyCreatedInstrs());
212+
reportGISelFailure(MF, TPC, MORE, "gisel-select", "cannot select",
213+
MI);
214+
return false;
215+
}
216+
LLVM_DEBUG(MIIMaintainer.reportFullyCreatedInstrs());
204217
}
205-
206-
// Dump the range of instructions that MI expanded into.
207-
LLVM_DEBUG({
208-
auto InsertedBegin = ReachedBegin ? MBB->begin() : std::next(MII);
209-
dbgs() << "Into:\n";
210-
for (auto &InsertedMI : make_range(InsertedBegin, AfterIt))
211-
dbgs() << " " << InsertedMI;
212-
dbgs() << '\n';
213-
});
214218
}
215219
}
216220

@@ -228,16 +232,10 @@ bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
228232
continue;
229233
}
230234
// Try to find redundant copies b/w vregs of the same register class.
231-
bool ReachedBegin = false;
232-
for (auto MII = std::prev(MBB.end()), Begin = MBB.begin(); !ReachedBegin;) {
233-
// Select this instruction.
235+
for (auto MII = MBB.rbegin(), End = MBB.rend(); MII != End;) {
234236
MachineInstr &MI = *MII;
237+
++MII;
235238

236-
// And have our iterator point to the next instruction, if there is one.
237-
if (MII == Begin)
238-
ReachedBegin = true;
239-
else
240-
--MII;
241239
if (MI.getOpcode() != TargetOpcode::COPY)
242240
continue;
243241
Register SrcReg = MI.getOperand(1).getReg();
@@ -342,3 +340,42 @@ bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
342340
// FIXME: Should we accurately track changes?
343341
return true;
344342
}
343+
344+
bool InstructionSelect::select(MachineInstr &MI) {
345+
MachineRegisterInfo &MRI = ISel->MF->getRegInfo();
346+
347+
// We could have folded this instruction away already, making it dead.
348+
// If so, erase it.
349+
if (isTriviallyDead(MI, MRI)) {
350+
LLVM_DEBUG(dbgs() << "Is dead.\n");
351+
salvageDebugInfo(MRI, MI);
352+
MI.eraseFromParent();
353+
return true;
354+
}
355+
356+
// Eliminate hints or G_CONSTANT_FOLD_BARRIER.
357+
if (isPreISelGenericOptimizationHint(MI.getOpcode()) ||
358+
MI.getOpcode() == TargetOpcode::G_CONSTANT_FOLD_BARRIER) {
359+
auto [DstReg, SrcReg] = MI.getFirst2Regs();
360+
361+
// At this point, the destination register class of the op may have
362+
// been decided.
363+
//
364+
// Propagate that through to the source register.
365+
const TargetRegisterClass *DstRC = MRI.getRegClassOrNull(DstReg);
366+
if (DstRC)
367+
MRI.setRegClass(SrcReg, DstRC);
368+
assert(canReplaceReg(DstReg, SrcReg, MRI) &&
369+
"Must be able to replace dst with src!");
370+
MI.eraseFromParent();
371+
MRI.replaceRegWith(DstReg, SrcReg);
372+
return true;
373+
}
374+
375+
if (MI.getOpcode() == TargetOpcode::G_INVOKE_REGION_START) {
376+
MI.eraseFromParent();
377+
return true;
378+
}
379+
380+
return ISel->select(MI);
381+
}

0 commit comments

Comments
 (0)