Skip to content

Commit 629db5d

Browse files
committed
[globalisel][combiner] Make the CombinerChangeObserver a MachineFunction::Delegate
Summary: This allows us to register it with the MachineFunction delegate and be notified automatically about erasure and creation of instructions. However, we still need explicit notification for modifications such as those caused by setReg() or replaceRegWith(). There is a catch with this though. The notification for creation is delivered before any operands can be added. While appropriate for scheduling combiner work. This is unfortunate for debug output since an opcode by itself doesn't provide sufficient information on what happened. As a result, the work list remembers the instructions (when debug output is requested) and emits a more complete dump later. Another nit is that the MachineFunction::Delegate provides const pointers which is inconvenient since we want to use it to schedule future modification. To resolve this GISelWorkList now has an optional pointer to the MachineFunction which describes the scope of the work it is permitted to schedule. If a given MachineInstr* is in this function then it is permitted to schedule work to be performed on the MachineInstr's. An alternative to this would be to remove the const from the MachineFunction::Delegate interface, however delegates are not permitted to modify the MachineInstr's they receive. In addition to this, the observer has three interface changes. * erasedInstr() is now erasingInstr() to indicate it is about to be erased but still exists at the moment. * changingInstr() and changedInstr() have been added to report changes before and after they are made. This allows us to trace the changes in the debug output. * As a convenience changingAllUsesOfReg() and finishedChangingAllUsesOfReg() will report changingInstr() and changedInstr() for each use of a given register. This is primarily useful for changes caused by MachineRegisterInfo::replaceRegWith() With this in place, both combine rules have been updated to report their changes to the observer. Finally, make some cosmetic changes to the debug output and make Combiner and CombinerHelp Reviewers: aditya_nandakumar, bogner, volkan, rtereshin, javed.absar Reviewed By: aditya_nandakumar Subscribers: mgorny, rovka, kristof.beyls, llvm-commits Differential Revision: https://reviews.llvm.org/D52947 llvm-svn: 349167
1 parent 95f90ef commit 629db5d

File tree

11 files changed

+251
-72
lines changed

11 files changed

+251
-72
lines changed

llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//== llvm/CodeGen/GlobalISel/CombinerHelper.h -------------- -*- C++ -*-==//
1+
//===-- llvm/CodeGen/GlobalISel/CombinerHelper.h --------------*- C++ -*-===//
22
//
33
// The LLVM Compiler Infrastructure
44
//
@@ -24,17 +24,24 @@ class GISelChangeObserver;
2424
class MachineIRBuilder;
2525
class MachineRegisterInfo;
2626
class MachineInstr;
27+
class MachineOperand;
2728

2829
class CombinerHelper {
2930
MachineIRBuilder &Builder;
3031
MachineRegisterInfo &MRI;
3132
GISelChangeObserver &Observer;
3233

33-
void scheduleForVisit(MachineInstr &MI);
34-
3534
public:
3635
CombinerHelper(GISelChangeObserver &Observer, MachineIRBuilder &B);
3736

37+
/// MachineRegisterInfo::replaceRegWith() and inform the observer of the changes
38+
void replaceRegWith(MachineRegisterInfo &MRI, unsigned FromReg, unsigned ToReg) const;
39+
40+
/// Replace a single register operand with a new register and inform the
41+
/// observer of the changes.
42+
void replaceRegOpWith(MachineRegisterInfo &MRI, MachineOperand &FromRegOp,
43+
unsigned ToReg) const;
44+
3845
/// If \p MI is COPY, try to combine it.
3946
/// Returns true if MI changed.
4047
bool tryCombineCopy(MachineInstr &MI);

llvm/include/llvm/CodeGen/GlobalISel/CombinerInfo.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,17 @@ class CombinerInfo {
4343
/// illegal ops that are created.
4444
bool LegalizeIllegalOps; // TODO: Make use of this.
4545
const LegalizerInfo *LInfo;
46+
47+
/// Attempt to combine instructions using MI as the root.
48+
///
49+
/// Use Observer to report the creation, modification, and erasure of
50+
/// instructions. GISelChangeObserver will automatically report certain
51+
/// kinds of operations. These operations are:
52+
/// * Instructions that are newly inserted into the MachineFunction
53+
/// * Instructions that are erased from the MachineFunction.
54+
///
55+
/// However, it is important to report instruction modification and this is
56+
/// not automatic.
4657
virtual bool combine(GISelChangeObserver &Observer, MachineInstr &MI,
4758
MachineIRBuilder &B) const = 0;
4859
};

llvm/include/llvm/CodeGen/GlobalISel/GISelChangeObserver.h

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
//== ----- llvm/CodeGen/GlobalISel/GISelChangeObserver.h ---------------------
2-
//== //
1+
//===----- llvm/CodeGen/GlobalISel/GISelChangeObserver.h ------------------===//
32
//
43
// The LLVM Compiler Infrastructure
54
//
@@ -15,25 +14,41 @@
1514
#ifndef LLVM_CODEGEN_GLOBALISEL_GISELCHANGEOBSERVER_H
1615
#define LLVM_CODEGEN_GLOBALISEL_GISELCHANGEOBSERVER_H
1716

17+
#include "llvm/ADT/SmallPtrSet.h"
18+
1819
namespace llvm {
20+
class MachineInstr;
21+
class MachineRegisterInfo;
22+
1923
/// Abstract class that contains various methods for clients to notify about
2024
/// changes. This should be the preferred way for APIs to notify changes.
2125
/// Typically calling erasingInstr/createdInstr multiple times should not affect
2226
/// the result. The observer would likely need to check if it was already
2327
/// notified earlier (consider using GISelWorkList).
24-
class MachineInstr;
2528
class GISelChangeObserver {
29+
SmallPtrSet<MachineInstr *, 4> ChangingAllUsesOfReg;
30+
2631
public:
2732
virtual ~GISelChangeObserver() {}
2833

2934
/// An instruction is about to be erased.
30-
virtual void erasingInstr(MachineInstr &MI) = 0;
35+
virtual void erasingInstr(const MachineInstr &MI) = 0;
3136
/// An instruction was created and inserted into the function.
32-
virtual void createdInstr(MachineInstr &MI) = 0;
37+
virtual void createdInstr(const MachineInstr &MI) = 0;
3338
/// This instruction is about to be mutated in some way.
34-
virtual void changingInstr(MachineInstr &MI) = 0;
39+
virtual void changingInstr(const MachineInstr &MI) = 0;
3540
/// This instruction was mutated in some way.
36-
virtual void changedInstr(MachineInstr &MI) = 0;
41+
virtual void changedInstr(const MachineInstr &MI) = 0;
42+
43+
/// All the instructions using the given register are being changed.
44+
/// For convenience, finishedChangingAllUsesOfReg() will report the completion
45+
/// of the changes. The use list may change between this call and
46+
/// finishedChangingAllUsesOfReg().
47+
void changingAllUsesOfReg(const MachineRegisterInfo &MRI, unsigned Reg);
48+
/// All instructions reported as changing by changingAllUsesOfReg() have
49+
/// finished being changed.
50+
void finishedChangingAllUsesOfReg();
51+
3752
};
3853

3954
} // namespace llvm

llvm/include/llvm/CodeGen/GlobalISel/GISelWorkList.h

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,60 @@ namespace llvm {
1818

1919
class MachineInstr;
2020

21-
// Worklist which mostly works similar to InstCombineWorkList, but on MachineInstrs.
22-
// The main difference with something like a SetVector is that erasing an element doesn't
23-
// move all elements over one place - instead just nulls out the element of the vector.
24-
// FIXME: Does it make sense to factor out common code with the instcombinerWorkList?
21+
// Worklist which mostly works similar to InstCombineWorkList, but on
22+
// MachineInstrs. The main difference with something like a SetVector is that
23+
// erasing an element doesn't move all elements over one place - instead just
24+
// nulls out the element of the vector.
25+
//
26+
// This worklist operates on instructions within a particular function. This is
27+
// important for acquiring the rights to modify/replace instructions a
28+
// GISelChangeObserver reports as the observer doesn't have the right to make
29+
// changes to the instructions it sees so we use our access to the
30+
// MachineFunction to establish that it's ok to add a given instruction to the
31+
// worklist.
32+
//
33+
// FIXME: Does it make sense to factor out common code with the
34+
// instcombinerWorkList?
2535
template<unsigned N>
2636
class GISelWorkList {
27-
SmallVector<MachineInstr*, N> Worklist;
28-
DenseMap<MachineInstr*, unsigned> WorklistMap;
37+
MachineFunction *MF;
38+
SmallVector<MachineInstr *, N> Worklist;
39+
DenseMap<MachineInstr *, unsigned> WorklistMap;
2940

3041
public:
31-
GISelWorkList() = default;
42+
GISelWorkList(MachineFunction *MF) : MF(MF) {}
3243

3344
bool empty() const { return WorklistMap.empty(); }
3445

3546
unsigned size() const { return WorklistMap.size(); }
3647

37-
/// Add - Add the specified instruction to the worklist if it isn't already
38-
/// in it.
48+
/// Add the specified instruction to the worklist if it isn't already in it.
3949
void insert(MachineInstr *I) {
40-
if (WorklistMap.try_emplace(I, Worklist.size()).second) {
41-
Worklist.push_back(I);
50+
// It would be safe to add this instruction to the worklist regardless but
51+
// for consistency with the const version, check that the instruction we're
52+
// adding would have been accepted if we were given a const pointer instead.
53+
insert(const_cast<const MachineInstr *>(I));
54+
}
55+
56+
void insert(const MachineInstr *I) {
57+
// Confirm we'd be able to find the non-const pointer we want to schedule if
58+
// we wanted to. We have the right to schedule work that may modify any
59+
// instruction in MF.
60+
assert(I->getParent() && "Expected parent BB");
61+
assert(I->getParent()->getParent() && "Expected parent function");
62+
assert((!MF || I->getParent()->getParent() == MF) &&
63+
"Expected parent function to be current function or not given");
64+
65+
// But don't actually do the search since we can derive it from the const
66+
// pointer.
67+
MachineInstr *NonConstI = const_cast<MachineInstr *>(I);
68+
if (WorklistMap.try_emplace(NonConstI, Worklist.size()).second) {
69+
Worklist.push_back(NonConstI);
4270
}
4371
}
4472

45-
/// Remove - remove I from the worklist if it exists.
46-
void remove(MachineInstr *I) {
73+
/// Remove I from the worklist if it exists.
74+
void remove(const MachineInstr *I) {
4775
auto It = WorklistMap.find(I);
4876
if (It == WorklistMap.end()) return; // Not in worklist.
4977

llvm/lib/CodeGen/GlobalISel/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ add_llvm_library(LLVMGlobalISel
33
GlobalISel.cpp
44
Combiner.cpp
55
CombinerHelper.cpp
6+
GISelChangeObserver.cpp
67
IRTranslator.cpp
78
InstructionSelect.cpp
89
InstructionSelector.cpp

llvm/lib/CodeGen/GlobalISel/Combiner.cpp

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===-- lib/CodeGen/GlobalISel/GICombiner.cpp -----------------------===//
1+
//===-- lib/CodeGen/GlobalISel/Combiner.cpp -------------------------------===//
22
//
33
// The LLVM Compiler Infrastructure
44
//
@@ -29,36 +29,62 @@ using namespace llvm;
2929
namespace {
3030
/// This class acts as the glue the joins the CombinerHelper to the overall
3131
/// Combine algorithm. The CombinerHelper is intended to report the
32-
/// modifications it makes to the MIR to the CombinerChangeObserver and the
32+
/// modifications it makes to the MIR to the GISelChangeObserver and the
3333
/// observer subclass will act on these events. In this case, instruction
3434
/// erasure will cancel any future visits to the erased instruction and
3535
/// instruction creation will schedule that instruction for a future visit.
3636
/// Other Combiner implementations may require more complex behaviour from
37-
/// their CombinerChangeObserver subclass.
38-
class WorkListMaintainer : public GISelChangeObserver {
37+
/// their GISelChangeObserver subclass.
38+
class WorkListMaintainer : public GISelChangeObserver,
39+
public MachineFunction::Delegate {
3940
using WorkListTy = GISelWorkList<512>;
41+
MachineFunction &MF;
4042
WorkListTy &WorkList;
43+
/// The instructions that have been created but we want to report once they
44+
/// have their operands. This is only maintained if debug output is requested.
45+
SmallPtrSet<const MachineInstr *, 4> CreatedInstrs;
4146

4247
public:
43-
WorkListMaintainer(WorkListTy &WorkList) : WorkList(WorkList) {}
44-
virtual ~WorkListMaintainer() {}
48+
WorkListMaintainer(MachineFunction &MF, WorkListTy &WorkList)
49+
: GISelChangeObserver(), MF(MF), WorkList(WorkList) {
50+
MF.setDelegate(this);
51+
}
52+
virtual ~WorkListMaintainer() {
53+
MF.resetDelegate(this);
54+
}
4555

46-
void erasingInstr(MachineInstr &MI) override {
56+
void erasingInstr(const MachineInstr &MI) override {
4757
LLVM_DEBUG(dbgs() << "Erased: " << MI << "\n");
4858
WorkList.remove(&MI);
4959
}
50-
void createdInstr(MachineInstr &MI) override {
51-
LLVM_DEBUG(dbgs() << "Created: " << MI << "\n");
60+
void createdInstr(const MachineInstr &MI) override {
61+
LLVM_DEBUG(dbgs() << "Creating: " << MI << "\n");
5262
WorkList.insert(&MI);
63+
LLVM_DEBUG(CreatedInstrs.insert(&MI));
5364
}
54-
void changingInstr(MachineInstr &MI) override {
65+
void changingInstr(const MachineInstr &MI) override {
5566
LLVM_DEBUG(dbgs() << "Changing: " << MI << "\n");
56-
WorkList.remove(&MI);
67+
WorkList.insert(&MI);
5768
}
58-
// Currently changed conservatively assumes erased.
59-
void changedInstr(MachineInstr &MI) override {
69+
void changedInstr(const MachineInstr &MI) override {
6070
LLVM_DEBUG(dbgs() << "Changed: " << MI << "\n");
61-
WorkList.remove(&MI);
71+
WorkList.insert(&MI);
72+
}
73+
74+
void reportFullyCreatedInstrs() {
75+
LLVM_DEBUG(for (const auto *MI
76+
: CreatedInstrs) {
77+
dbgs() << "Created: ";
78+
MI->print(dbgs());
79+
});
80+
LLVM_DEBUG(CreatedInstrs.clear());
81+
}
82+
83+
void MF_HandleInsertion(const MachineInstr &MI) override {
84+
createdInstr(MI);
85+
}
86+
void MF_HandleRemoval(const MachineInstr &MI) override {
87+
erasingInstr(MI);
6288
}
6389
};
6490
}
@@ -90,8 +116,8 @@ bool Combiner::combineMachineInstrs(MachineFunction &MF) {
90116
// insert with list bottom up, so while we pop_back_val, we'll traverse top
91117
// down RPOT.
92118
Changed = false;
93-
GISelWorkList<512> WorkList;
94-
WorkListMaintainer Observer(WorkList);
119+
GISelWorkList<512> WorkList(&MF);
120+
WorkListMaintainer Observer(MF, WorkList);
95121
for (MachineBasicBlock *MBB : post_order(&MF)) {
96122
if (MBB->empty())
97123
continue;
@@ -110,8 +136,9 @@ bool Combiner::combineMachineInstrs(MachineFunction &MF) {
110136
// Main Loop. Process the instructions here.
111137
while (!WorkList.empty()) {
112138
MachineInstr *CurrInst = WorkList.pop_back_val();
113-
LLVM_DEBUG(dbgs() << "Try combining " << *CurrInst << "\n";);
139+
LLVM_DEBUG(dbgs() << "\nTry combining " << *CurrInst;);
114140
Changed |= CInfo.combine(Observer, *CurrInst, Builder);
141+
Observer.reportFullyCreatedInstrs();
115142
}
116143
MFChanged |= Changed;
117144
} while (Changed);

0 commit comments

Comments
 (0)