Skip to content

Commit d2336fd

Browse files
[RFC][GlobalISel] InstructionSelect: Allow arbitrary instruction erasure (#97670)
See https://discourse.llvm.org/t/rfc-globalisel-instructionselect-allow-arbitrary-instruction-erasure
1 parent 94e6786 commit d2336fd

File tree

5 files changed

+220
-94
lines changed

5 files changed

+220
-94
lines changed

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

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

2121
namespace llvm {
2222

23+
class InstructionSelector;
24+
class GISelKnownBits;
2325
class BlockFrequencyInfo;
2426
class ProfileSummaryInfo;
2527

@@ -53,12 +55,20 @@ class InstructionSelect : public MachineFunctionPass {
5355
char &PassID = ID);
5456

5557
bool runOnMachineFunction(MachineFunction &MF) override;
58+
bool selectMachineFunction(MachineFunction &MF);
59+
void setInstructionSelector(InstructionSelector *NewISel) { ISel = NewISel; }
5660

5761
protected:
62+
class MIIteratorMaintainer;
63+
64+
InstructionSelector *ISel = nullptr;
65+
GISelKnownBits *KB = nullptr;
5866
BlockFrequencyInfo *BFI = nullptr;
5967
ProfileSummaryInfo *PSI = nullptr;
6068

6169
CodeGenOptLevel OptLevel = CodeGenOptLevel::None;
70+
71+
bool selectInstr(MachineInstr &MI);
6272
};
6373
} // End namespace llvm.
6474

llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,9 @@ class InstructionSelector : public GIMatchTableExecutor {
3232
/// !isPreISelGenericOpcode(I.getOpcode())
3333
virtual bool select(MachineInstr &I) = 0;
3434

35-
void setTargetPassConfig(const TargetPassConfig *T) { TPC = T; }
36-
37-
void setRemarkEmitter(MachineOptimizationRemarkEmitter *M) { MORE = M; }
38-
39-
protected:
35+
// FIXME: Eliminate dependency on TargetPassConfig for NewPM transition
4036
const TargetPassConfig *TPC = nullptr;
37+
4138
MachineOptimizationRemarkEmitter *MORE = nullptr;
4239
};
4340
} // namespace llvm

llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp

Lines changed: 131 additions & 89 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"
@@ -65,6 +67,52 @@ INITIALIZE_PASS_END(InstructionSelect, DEBUG_TYPE,
6567
InstructionSelect::InstructionSelect(CodeGenOptLevel OL, char &PassID)
6668
: MachineFunctionPass(PassID), OptLevel(OL) {}
6769

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

87-
LLVM_DEBUG(dbgs() << "Selecting function: " << MF.getName() << '\n');
88-
89-
const TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
90-
InstructionSelector *ISel = MF.getSubtarget().getInstructionSelector();
91-
ISel->setTargetPassConfig(&TPC);
135+
ISel = MF.getSubtarget().getInstructionSelector();
136+
ISel->TPC = &getAnalysis<TargetPassConfig>();
92137

138+
// FIXME: Properly override OptLevel in TargetMachine. See OptLevelChanger
93139
CodeGenOptLevel OldOptLevel = OptLevel;
94140
auto RestoreOptLevel = make_scope_exit([=]() { OptLevel = OldOptLevel; });
95141
OptLevel = MF.getFunction().hasOptNone() ? CodeGenOptLevel::None
96142
: MF.getTarget().getOptLevel();
97143

98-
GISelKnownBits *KB = &getAnalysis<GISelKnownBitsAnalysis>().get(MF);
144+
KB = &getAnalysis<GISelKnownBitsAnalysis>().get(MF);
99145
if (OptLevel != CodeGenOptLevel::None) {
100146
PSI = &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI();
101147
if (PSI && PSI->hasProfileSummary())
102148
BFI = &getAnalysis<LazyBlockFrequencyInfoPass>().getBFI();
103149
}
104150

105-
CodeGenCoverage CoverageInfo;
151+
return selectMachineFunction(MF);
152+
}
153+
154+
bool InstructionSelect::selectMachineFunction(MachineFunction &MF) {
155+
LLVM_DEBUG(dbgs() << "Selecting function: " << MF.getName() << '\n');
106156
assert(ISel && "Cannot work without InstructionSelector");
157+
158+
const TargetPassConfig &TPC = *ISel->TPC;
159+
CodeGenCoverage CoverageInfo;
107160
ISel->setupMF(MF, KB, &CoverageInfo, PSI, BFI);
108161

109162
// An optimization remark emitter. Used to report failures.
110163
MachineOptimizationRemarkEmitter MORE(MF, /*MBFI=*/nullptr);
111-
ISel->setRemarkEmitter(&MORE);
164+
ISel->MORE = &MORE;
112165

113166
// FIXME: There are many other MF/MFI fields we need to initialize.
114167

@@ -131,80 +184,36 @@ bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
131184
// Keep track of selected blocks, so we can delete unreachable ones later.
132185
DenseSet<MachineBasicBlock *> SelectedBlocks;
133186

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

@@ -222,16 +231,10 @@ bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) {
222231
continue;
223232
}
224233
// Try to find redundant copies b/w vregs of the same register class.
225-
bool ReachedBegin = false;
226-
for (auto MII = std::prev(MBB.end()), Begin = MBB.begin(); !ReachedBegin;) {
227-
// Select this instruction.
234+
for (auto MII = MBB.rbegin(), End = MBB.rend(); MII != End;) {
228235
MachineInstr &MI = *MII;
236+
++MII;
229237

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

llvm/unittests/CodeGen/GlobalISel/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ add_llvm_unittest(GlobalISelTests
2828
GISelUtilsTest.cpp
2929
GISelAliasTest.cpp
3030
CallLowering.cpp
31+
InstructionSelectTest.cpp
3132
)

0 commit comments

Comments
 (0)