Skip to content
This repository was archived by the owner on Oct 24, 2019. It is now read-only.

Commit 66f2370

Browse files
author
Aditya Nandakumar
committed
[GISel]: Add support for CSEing continuously during GISel passes.
https://reviews.llvm.org/D52803 This patch adds support to continuously CSE instructions during each of the GISel passes. It consists of a GISelCSEInfo analysis pass that can be used by the CSEMIRBuilder. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351283 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent bbf3052 commit 66f2370

34 files changed

+1499
-231
lines changed
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
//===- llvm/CodeGen/GlobalISel/CSEInfo.h ------------------*- C++ -*-===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
/// Provides analysis for continuously CSEing during GISel passes.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
#ifndef LLVM_CODEGEN_GLOBALISEL_CSEINFO_H
14+
#define LLVM_CODEGEN_GLOBALISEL_CSEINFO_H
15+
16+
#include "llvm/ADT/FoldingSet.h"
17+
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
18+
#include "llvm/CodeGen/GlobalISel/GISelWorkList.h"
19+
#include "llvm/CodeGen/GlobalISel/Utils.h"
20+
#include "llvm/CodeGen/MachineBasicBlock.h"
21+
#include "llvm/CodeGen/MachineFunctionPass.h"
22+
#include "llvm/IR/PassManager.h"
23+
#include "llvm/Pass.h"
24+
#include "llvm/Support/Allocator.h"
25+
26+
namespace llvm {
27+
28+
/// A class that wraps MachineInstrs and derives from FoldingSetNode in order to
29+
/// be uniqued in a CSEMap. The tradeoff here is extra memory allocations for
30+
/// UniqueMachineInstr vs making MachineInstr bigger.
31+
class UniqueMachineInstr : public FoldingSetNode {
32+
friend class GISelCSEInfo;
33+
const MachineInstr *MI;
34+
explicit UniqueMachineInstr(const MachineInstr *MI) : MI(MI) {}
35+
36+
public:
37+
void Profile(FoldingSetNodeID &ID);
38+
};
39+
40+
// Class representing some configuration that can be done during CSE analysis.
41+
// Currently it only supports shouldCSE method that each pass can set.
42+
class CSEConfig {
43+
public:
44+
virtual ~CSEConfig() = default;
45+
// Hook for defining which Generic instructions should be CSEd.
46+
// GISelCSEInfo currently only calls this hook when dealing with generic
47+
// opcodes.
48+
virtual bool shouldCSEOpc(unsigned Opc);
49+
};
50+
51+
// TODO: Find a better place for this.
52+
// Commonly used for O0 config.
53+
class CSEConfigConstantOnly : public CSEConfig {
54+
public:
55+
virtual ~CSEConfigConstantOnly() = default;
56+
virtual bool shouldCSEOpc(unsigned Opc) override;
57+
};
58+
59+
/// The CSE Analysis object.
60+
/// This installs itself as a delegate to the MachineFunction to track
61+
/// new instructions as well as deletions. It however will not be able to
62+
/// track instruction mutations. In such cases, recordNewInstruction should be
63+
/// called (for eg inside MachineIRBuilder::recordInsertion).
64+
/// Also because of how just the instruction can be inserted without adding any
65+
/// operands to the instruction, instructions are uniqued and inserted lazily.
66+
/// CSEInfo should assert when trying to enter an incomplete instruction into
67+
/// the CSEMap. There is Opcode level granularity on which instructions can be
68+
/// CSE'd and for now, only Generic instructions are CSEable.
69+
class GISelCSEInfo : public GISelChangeObserver {
70+
// Make it accessible only to CSEMIRBuilder.
71+
friend class CSEMIRBuilder;
72+
73+
BumpPtrAllocator UniqueInstrAllocator;
74+
FoldingSet<UniqueMachineInstr> CSEMap;
75+
MachineRegisterInfo *MRI = nullptr;
76+
MachineFunction *MF = nullptr;
77+
std::unique_ptr<CSEConfig> CSEOpt;
78+
/// Keep a cache of UniqueInstrs for each MachineInstr. In GISel,
79+
/// often instructions are mutated (while their ID has completely changed).
80+
/// Whenever mutation happens, invalidate the UniqueMachineInstr for the
81+
/// MachineInstr
82+
DenseMap<const MachineInstr *, UniqueMachineInstr *> InstrMapping;
83+
84+
/// Store instructions that are not fully formed in TemporaryInsts.
85+
/// Also because CSE insertion happens lazily, we can remove insts from this
86+
/// list and avoid inserting and then removing from the CSEMap.
87+
GISelWorkList<8> TemporaryInsts;
88+
89+
// Only used in asserts.
90+
DenseMap<unsigned, unsigned> OpcodeHitTable;
91+
92+
bool isUniqueMachineInstValid(const UniqueMachineInstr &UMI) const;
93+
94+
void invalidateUniqueMachineInstr(UniqueMachineInstr *UMI);
95+
96+
UniqueMachineInstr *getNodeIfExists(FoldingSetNodeID &ID,
97+
MachineBasicBlock *MBB, void *&InsertPos);
98+
99+
/// Allocate and construct a new UniqueMachineInstr for MI and return.
100+
UniqueMachineInstr *getUniqueInstrForMI(const MachineInstr *MI);
101+
102+
void insertNode(UniqueMachineInstr *UMI, void *InsertPos = nullptr);
103+
104+
/// Get the MachineInstr(Unique) if it exists already in the CSEMap and the
105+
/// same MachineBasicBlock.
106+
MachineInstr *getMachineInstrIfExists(FoldingSetNodeID &ID,
107+
MachineBasicBlock *MBB,
108+
void *&InsertPos);
109+
110+
/// Use this method to allocate a new UniqueMachineInstr for MI and insert it
111+
/// into the CSEMap. MI should return true for shouldCSE(MI->getOpcode())
112+
void insertInstr(MachineInstr *MI, void *InsertPos = nullptr);
113+
114+
public:
115+
GISelCSEInfo() = default;
116+
117+
virtual ~GISelCSEInfo();
118+
119+
void setMF(MachineFunction &MF);
120+
121+
/// Records a newly created inst in a list and lazily insert it to the CSEMap.
122+
/// Sometimes, this method might be called with a partially constructed
123+
/// MachineInstr,
124+
// (right after BuildMI without adding any operands) - and in such cases,
125+
// defer the hashing of the instruction to a later stage.
126+
void recordNewInstruction(MachineInstr *MI);
127+
128+
/// Use this callback to inform CSE about a newly fully created instruction.
129+
void handleRecordedInst(MachineInstr *MI);
130+
131+
/// Use this callback to insert all the recorded instructions. At this point,
132+
/// all of these insts need to be fully constructed and should not be missing
133+
/// any operands.
134+
void handleRecordedInsts();
135+
136+
/// Remove this inst from the CSE map. If this inst has not been inserted yet,
137+
/// it will be removed from the Tempinsts list if it exists.
138+
void handleRemoveInst(MachineInstr *MI);
139+
140+
void releaseMemory();
141+
142+
void setCSEConfig(std::unique_ptr<CSEConfig> Opt) { CSEOpt = std::move(Opt); }
143+
144+
bool shouldCSE(unsigned Opc) const;
145+
146+
void analyze(MachineFunction &MF);
147+
148+
void countOpcodeHit(unsigned Opc);
149+
150+
void print();
151+
152+
// Observer API
153+
void erasingInstr(MachineInstr &MI) override;
154+
void createdInstr(MachineInstr &MI) override;
155+
void changingInstr(MachineInstr &MI) override;
156+
void changedInstr(MachineInstr &MI) override;
157+
};
158+
159+
class TargetRegisterClass;
160+
class RegisterBank;
161+
162+
// Simple builder class to easily profile properties about MIs.
163+
class GISelInstProfileBuilder {
164+
FoldingSetNodeID &ID;
165+
const MachineRegisterInfo &MRI;
166+
167+
public:
168+
GISelInstProfileBuilder(FoldingSetNodeID &ID, const MachineRegisterInfo &MRI)
169+
: ID(ID), MRI(MRI) {}
170+
// Profiling methods.
171+
const GISelInstProfileBuilder &addNodeIDOpcode(unsigned Opc) const;
172+
const GISelInstProfileBuilder &addNodeIDRegType(const LLT &Ty) const;
173+
const GISelInstProfileBuilder &addNodeIDRegType(const unsigned) const;
174+
175+
const GISelInstProfileBuilder &
176+
addNodeIDRegType(const TargetRegisterClass *RC) const;
177+
const GISelInstProfileBuilder &addNodeIDRegType(const RegisterBank *RB) const;
178+
179+
const GISelInstProfileBuilder &addNodeIDRegNum(unsigned Reg) const;
180+
181+
const GISelInstProfileBuilder &addNodeIDImmediate(int64_t Imm) const;
182+
const GISelInstProfileBuilder &
183+
addNodeIDMBB(const MachineBasicBlock *MBB) const;
184+
185+
const GISelInstProfileBuilder &
186+
addNodeIDMachineOperand(const MachineOperand &MO) const;
187+
188+
const GISelInstProfileBuilder &addNodeIDFlag(unsigned Flag) const;
189+
const GISelInstProfileBuilder &addNodeID(const MachineInstr *MI) const;
190+
};
191+
192+
/// Simple wrapper that does the following.
193+
/// 1) Lazily evaluate the MachineFunction to compute CSEable instructions.
194+
/// 2) Allows configuration of which instructions are CSEd through CSEConfig
195+
/// object. Provides a method called get which takes a CSEConfig object.
196+
class GISelCSEAnalysisWrapper {
197+
GISelCSEInfo Info;
198+
MachineFunction *MF = nullptr;
199+
bool AlreadyComputed = false;
200+
201+
public:
202+
/// Takes a CSEConfig object that defines what opcodes get CSEd.
203+
/// If CSEConfig is already set, and the CSE Analysis has been preserved,
204+
/// it will not use the new CSEOpt(use Recompute to force using the new
205+
/// CSEOpt).
206+
GISelCSEInfo &get(std::unique_ptr<CSEConfig> CSEOpt, bool ReCompute = false);
207+
void setMF(MachineFunction &MFunc) { MF = &MFunc; }
208+
void setComputed(bool Computed) { AlreadyComputed = Computed; }
209+
void releaseMemory() { Info.releaseMemory(); }
210+
};
211+
212+
/// The actual analysis pass wrapper.
213+
class GISelCSEAnalysisWrapperPass : public MachineFunctionPass {
214+
GISelCSEAnalysisWrapper Wrapper;
215+
216+
public:
217+
static char ID;
218+
GISelCSEAnalysisWrapperPass() : MachineFunctionPass(ID) {
219+
initializeGISelCSEAnalysisWrapperPassPass(*PassRegistry::getPassRegistry());
220+
}
221+
222+
void getAnalysisUsage(AnalysisUsage &AU) const override;
223+
224+
const GISelCSEAnalysisWrapper &getCSEWrapper() const { return Wrapper; }
225+
GISelCSEAnalysisWrapper &getCSEWrapper() { return Wrapper; }
226+
227+
bool runOnMachineFunction(MachineFunction &MF) override;
228+
229+
void releaseMemory() override {
230+
Wrapper.releaseMemory();
231+
Wrapper.setComputed(false);
232+
}
233+
};
234+
235+
} // namespace llvm
236+
237+
#endif
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//===-- llvm/CodeGen/GlobalISel/CSEMIRBuilder.h --*- C++ -*-==//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
/// \file
10+
/// This file implements a version of MachineIRBuilder which CSEs insts within
11+
/// a MachineBasicBlock.
12+
//===----------------------------------------------------------------------===//
13+
#ifndef LLVM_CODEGEN_GLOBALISEL_CSEMIRBUILDER_H
14+
#define LLVM_CODEGEN_GLOBALISEL_CSEMIRBUILDER_H
15+
16+
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
17+
#include "llvm/CodeGen/GlobalISel/Utils.h"
18+
19+
namespace llvm {
20+
21+
/// Defines a builder that does CSE of MachineInstructions using GISelCSEInfo.
22+
/// Eg usage.
23+
///
24+
///
25+
/// GISelCSEInfo *Info =
26+
/// &getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEInfo(); CSEMIRBuilder
27+
/// CB(Builder.getState()); CB.setCSEInfo(Info); auto A = CB.buildConstant(s32,
28+
/// 42); auto B = CB.buildConstant(s32, 42); assert(A == B); unsigned CReg =
29+
/// MRI.createGenericVirtualRegister(s32); auto C = CB.buildConstant(CReg, 42);
30+
/// assert(C->getOpcode() == TargetOpcode::COPY);
31+
/// Explicitly passing in a register would materialize a copy if possible.
32+
/// CSEMIRBuilder also does trivial constant folding for binary ops.
33+
class CSEMIRBuilder : public MachineIRBuilder {
34+
35+
/// Returns true if A dominates B (within the same basic block).
36+
/// Both iterators must be in the same basic block.
37+
//
38+
// TODO: Another approach for checking dominance is having two iterators and
39+
// making them go towards each other until they meet or reach begin/end. Which
40+
// approach is better? Should this even change dynamically? For G_CONSTANTS
41+
// most of which will be at the top of the BB, the top down approach would be
42+
// a better choice. Does IRTranslator placing constants at the beginning still
43+
// make sense? Should this change based on Opcode?
44+
bool dominates(MachineBasicBlock::const_iterator A,
45+
MachineBasicBlock::const_iterator B) const;
46+
47+
/// For given ID, find a machineinstr in the CSE Map. If found, check if it
48+
/// dominates the current insertion point and if not, move it just before the
49+
/// current insertion point and return it. If not found, return Null
50+
/// MachineInstrBuilder.
51+
MachineInstrBuilder getDominatingInstrForID(FoldingSetNodeID &ID,
52+
void *&NodeInsertPos);
53+
/// Simple check if we can CSE (we have the CSEInfo) or if this Opcode is
54+
/// safe to CSE.
55+
bool canPerformCSEForOpc(unsigned Opc) const;
56+
57+
void profileDstOp(const DstOp &Op, GISelInstProfileBuilder &B) const;
58+
59+
void profileDstOps(ArrayRef<DstOp> Ops, GISelInstProfileBuilder &B) const {
60+
for (const DstOp &Op : Ops)
61+
profileDstOp(Op, B);
62+
}
63+
64+
void profileSrcOp(const SrcOp &Op, GISelInstProfileBuilder &B) const;
65+
66+
void profileSrcOps(ArrayRef<SrcOp> Ops, GISelInstProfileBuilder &B) const {
67+
for (const SrcOp &Op : Ops)
68+
profileSrcOp(Op, B);
69+
}
70+
71+
void profileMBBOpcode(GISelInstProfileBuilder &B, unsigned Opc) const;
72+
73+
void profileEverything(unsigned Opc, ArrayRef<DstOp> DstOps,
74+
ArrayRef<SrcOp> SrcOps, Optional<unsigned> Flags,
75+
GISelInstProfileBuilder &B) const;
76+
77+
// Takes a MachineInstrBuilder and inserts it into the CSEMap using the
78+
// NodeInsertPos.
79+
MachineInstrBuilder memoizeMI(MachineInstrBuilder MIB, void *NodeInsertPos);
80+
81+
// If we have can CSE an instruction, but still need to materialize to a VReg,
82+
// we emit a copy from the CSE'd inst to the VReg.
83+
MachineInstrBuilder generateCopiesIfRequired(ArrayRef<DstOp> DstOps,
84+
MachineInstrBuilder &MIB);
85+
86+
// If we have can CSE an instruction, but still need to materialize to a VReg,
87+
// check if we can generate copies. It's not possible to return a single MIB,
88+
// while emitting copies to multiple vregs.
89+
bool checkCopyToDefsPossible(ArrayRef<DstOp> DstOps);
90+
91+
public:
92+
// Pull in base class constructors.
93+
using MachineIRBuilder::MachineIRBuilder;
94+
// Unhide buildInstr
95+
MachineInstrBuilder buildInstr(unsigned Opc, ArrayRef<DstOp> DstOps,
96+
ArrayRef<SrcOp> SrcOps,
97+
Optional<unsigned> Flag = None) override;
98+
// Bring in the other overload from the base class.
99+
using MachineIRBuilder::buildConstant;
100+
101+
MachineInstrBuilder buildConstant(const DstOp &Res,
102+
const ConstantInt &Val) override;
103+
104+
// Bring in the other overload from the base class.
105+
using MachineIRBuilder::buildFConstant;
106+
MachineInstrBuilder buildFConstant(const DstOp &Res,
107+
const ConstantFP &Val) override;
108+
};
109+
} // namespace llvm
110+
#endif

include/llvm/CodeGen/GlobalISel/Combiner.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,25 @@
2121
namespace llvm {
2222
class MachineRegisterInfo;
2323
class CombinerInfo;
24+
class GISelCSEInfo;
2425
class TargetPassConfig;
2526
class MachineFunction;
2627

2728
class Combiner {
2829
public:
2930
Combiner(CombinerInfo &CombinerInfo, const TargetPassConfig *TPC);
3031

31-
bool combineMachineInstrs(MachineFunction &MF);
32+
/// If CSEInfo is not null, then the Combiner will setup observer for
33+
/// CSEInfo and instantiate a CSEMIRBuilder. Pass nullptr if CSE is not
34+
/// needed.
35+
bool combineMachineInstrs(MachineFunction &MF, GISelCSEInfo *CSEInfo);
3236

3337
protected:
3438
CombinerInfo &CInfo;
3539

3640
MachineRegisterInfo *MRI = nullptr;
3741
const TargetPassConfig *TPC;
38-
MachineIRBuilder Builder;
42+
std::unique_ptr<MachineIRBuilder> Builder;
3943
};
4044

4145
} // End namespace llvm.

0 commit comments

Comments
 (0)