xref: /src/contrib/llvm-project/llvm/lib/CodeGen/FixupStatepointCallerSaved.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1cfca06d7SDimitry Andric //===-- FixupStatepointCallerSaved.cpp - Fixup caller saved registers  ----===//
2cfca06d7SDimitry Andric //
3c0981da4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c0981da4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5c0981da4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6cfca06d7SDimitry Andric //
7cfca06d7SDimitry Andric //===----------------------------------------------------------------------===//
8cfca06d7SDimitry Andric ///
9cfca06d7SDimitry Andric /// \file
10cfca06d7SDimitry Andric /// Statepoint instruction in deopt parameters contains values which are
11cfca06d7SDimitry Andric /// meaningful to the runtime and should be able to be read at the moment the
12cfca06d7SDimitry Andric /// call returns. So we can say that we need to encode the fact that these
13cfca06d7SDimitry Andric /// values are "late read" by runtime. If we could express this notion for
14cfca06d7SDimitry Andric /// register allocator it would produce the right form for us.
15cfca06d7SDimitry Andric /// The need to fixup (i.e this pass) is specifically handling the fact that
16cfca06d7SDimitry Andric /// we cannot describe such a late read for the register allocator.
17cfca06d7SDimitry Andric /// Register allocator may put the value on a register clobbered by the call.
18cfca06d7SDimitry Andric /// This pass forces the spill of such registers and replaces corresponding
19cfca06d7SDimitry Andric /// statepoint operands to added spill slots.
20cfca06d7SDimitry Andric ///
21cfca06d7SDimitry Andric //===----------------------------------------------------------------------===//
22cfca06d7SDimitry Andric 
23cfca06d7SDimitry Andric #include "llvm/ADT/SmallSet.h"
24cfca06d7SDimitry Andric #include "llvm/ADT/Statistic.h"
25cfca06d7SDimitry Andric #include "llvm/CodeGen/MachineFrameInfo.h"
26cfca06d7SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
27cfca06d7SDimitry Andric #include "llvm/CodeGen/StackMaps.h"
28cfca06d7SDimitry Andric #include "llvm/CodeGen/TargetInstrInfo.h"
29cfca06d7SDimitry Andric #include "llvm/IR/Statepoint.h"
30cfca06d7SDimitry Andric #include "llvm/InitializePasses.h"
31cfca06d7SDimitry Andric #include "llvm/Support/Debug.h"
32cfca06d7SDimitry Andric 
33cfca06d7SDimitry Andric using namespace llvm;
34cfca06d7SDimitry Andric 
35cfca06d7SDimitry Andric #define DEBUG_TYPE "fixup-statepoint-caller-saved"
36cfca06d7SDimitry Andric STATISTIC(NumSpilledRegisters, "Number of spilled register");
37cfca06d7SDimitry Andric STATISTIC(NumSpillSlotsAllocated, "Number of spill slots allocated");
38cfca06d7SDimitry Andric STATISTIC(NumSpillSlotsExtended, "Number of spill slots extended");
39cfca06d7SDimitry Andric 
40cfca06d7SDimitry Andric static cl::opt<bool> FixupSCSExtendSlotSize(
41cfca06d7SDimitry Andric     "fixup-scs-extend-slot-size", cl::Hidden, cl::init(false),
42cfca06d7SDimitry Andric     cl::desc("Allow spill in spill slot of greater size than register size"),
43cfca06d7SDimitry Andric     cl::Hidden);
44cfca06d7SDimitry Andric 
45b60736ecSDimitry Andric static cl::opt<bool> PassGCPtrInCSR(
46b60736ecSDimitry Andric     "fixup-allow-gcptr-in-csr", cl::Hidden, cl::init(false),
47b60736ecSDimitry Andric     cl::desc("Allow passing GC Pointer arguments in callee saved registers"));
48b60736ecSDimitry Andric 
49b60736ecSDimitry Andric static cl::opt<bool> EnableCopyProp(
50b60736ecSDimitry Andric     "fixup-scs-enable-copy-propagation", cl::Hidden, cl::init(true),
51b60736ecSDimitry Andric     cl::desc("Enable simple copy propagation during register reloading"));
52b60736ecSDimitry Andric 
53b60736ecSDimitry Andric // This is purely debugging option.
54b60736ecSDimitry Andric // It may be handy for investigating statepoint spilling issues.
55b60736ecSDimitry Andric static cl::opt<unsigned> MaxStatepointsWithRegs(
56b60736ecSDimitry Andric     "fixup-max-csr-statepoints", cl::Hidden,
57b60736ecSDimitry Andric     cl::desc("Max number of statepoints allowed to pass GC Ptrs in registers"));
58b60736ecSDimitry Andric 
59cfca06d7SDimitry Andric namespace {
60cfca06d7SDimitry Andric 
61cfca06d7SDimitry Andric class FixupStatepointCallerSaved : public MachineFunctionPass {
62cfca06d7SDimitry Andric public:
63cfca06d7SDimitry Andric   static char ID;
64cfca06d7SDimitry Andric 
FixupStatepointCallerSaved()65cfca06d7SDimitry Andric   FixupStatepointCallerSaved() : MachineFunctionPass(ID) {
66cfca06d7SDimitry Andric     initializeFixupStatepointCallerSavedPass(*PassRegistry::getPassRegistry());
67cfca06d7SDimitry Andric   }
68cfca06d7SDimitry Andric 
getAnalysisUsage(AnalysisUsage & AU) const69cfca06d7SDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
70cfca06d7SDimitry Andric     AU.setPreservesCFG();
71cfca06d7SDimitry Andric     MachineFunctionPass::getAnalysisUsage(AU);
72cfca06d7SDimitry Andric   }
73cfca06d7SDimitry Andric 
getPassName() const74cfca06d7SDimitry Andric   StringRef getPassName() const override {
75cfca06d7SDimitry Andric     return "Fixup Statepoint Caller Saved";
76cfca06d7SDimitry Andric   }
77cfca06d7SDimitry Andric 
78cfca06d7SDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
79cfca06d7SDimitry Andric };
80b60736ecSDimitry Andric 
81cfca06d7SDimitry Andric } // End anonymous namespace.
82cfca06d7SDimitry Andric 
83cfca06d7SDimitry Andric char FixupStatepointCallerSaved::ID = 0;
84cfca06d7SDimitry Andric char &llvm::FixupStatepointCallerSavedID = FixupStatepointCallerSaved::ID;
85cfca06d7SDimitry Andric 
86cfca06d7SDimitry Andric INITIALIZE_PASS_BEGIN(FixupStatepointCallerSaved, DEBUG_TYPE,
87cfca06d7SDimitry Andric                       "Fixup Statepoint Caller Saved", false, false)
88cfca06d7SDimitry Andric INITIALIZE_PASS_END(FixupStatepointCallerSaved, DEBUG_TYPE,
89cfca06d7SDimitry Andric                     "Fixup Statepoint Caller Saved", false, false)
90cfca06d7SDimitry Andric 
91cfca06d7SDimitry Andric // Utility function to get size of the register.
getRegisterSize(const TargetRegisterInfo & TRI,Register Reg)92cfca06d7SDimitry Andric static unsigned getRegisterSize(const TargetRegisterInfo &TRI, Register Reg) {
93cfca06d7SDimitry Andric   const TargetRegisterClass *RC = TRI.getMinimalPhysRegClass(Reg);
94cfca06d7SDimitry Andric   return TRI.getSpillSize(*RC);
95cfca06d7SDimitry Andric }
96cfca06d7SDimitry Andric 
97b60736ecSDimitry Andric // Try to eliminate redundant copy to register which we're going to
98b60736ecSDimitry Andric // spill, i.e. try to change:
99b60736ecSDimitry Andric //    X = COPY Y
100b60736ecSDimitry Andric //    SPILL X
101b60736ecSDimitry Andric //  to
102b60736ecSDimitry Andric //    SPILL Y
103b60736ecSDimitry Andric //  If there are no uses of X between copy and STATEPOINT, that COPY
104b60736ecSDimitry Andric //  may be eliminated.
105b60736ecSDimitry Andric //  Reg - register we're about to spill
106b60736ecSDimitry Andric //  RI - On entry points to statepoint.
107b60736ecSDimitry Andric //       On successful copy propagation set to new spill point.
108b60736ecSDimitry Andric //  IsKill - set to true if COPY is Kill (there are no uses of Y)
109b60736ecSDimitry Andric //  Returns either found source copy register or original one.
performCopyPropagation(Register Reg,MachineBasicBlock::iterator & RI,bool & IsKill,const TargetInstrInfo & TII,const TargetRegisterInfo & TRI)110b60736ecSDimitry Andric static Register performCopyPropagation(Register Reg,
111b60736ecSDimitry Andric                                        MachineBasicBlock::iterator &RI,
112b60736ecSDimitry Andric                                        bool &IsKill, const TargetInstrInfo &TII,
113b60736ecSDimitry Andric                                        const TargetRegisterInfo &TRI) {
114b60736ecSDimitry Andric   // First check if statepoint itself uses Reg in non-meta operands.
115ac9a064cSDimitry Andric   int Idx = RI->findRegisterUseOperandIdx(Reg, &TRI, false);
116b60736ecSDimitry Andric   if (Idx >= 0 && (unsigned)Idx < StatepointOpers(&*RI).getNumDeoptArgsIdx()) {
117b60736ecSDimitry Andric     IsKill = false;
118b60736ecSDimitry Andric     return Reg;
119b60736ecSDimitry Andric   }
120b60736ecSDimitry Andric 
121b60736ecSDimitry Andric   if (!EnableCopyProp)
122b60736ecSDimitry Andric     return Reg;
123b60736ecSDimitry Andric 
124b60736ecSDimitry Andric   MachineBasicBlock *MBB = RI->getParent();
125b60736ecSDimitry Andric   MachineBasicBlock::reverse_iterator E = MBB->rend();
126b60736ecSDimitry Andric   MachineInstr *Def = nullptr, *Use = nullptr;
127b60736ecSDimitry Andric   for (auto It = ++(RI.getReverse()); It != E; ++It) {
128b60736ecSDimitry Andric     if (It->readsRegister(Reg, &TRI) && !Use)
129b60736ecSDimitry Andric       Use = &*It;
130b60736ecSDimitry Andric     if (It->modifiesRegister(Reg, &TRI)) {
131b60736ecSDimitry Andric       Def = &*It;
132b60736ecSDimitry Andric       break;
133b60736ecSDimitry Andric     }
134b60736ecSDimitry Andric   }
135b60736ecSDimitry Andric 
136b60736ecSDimitry Andric   if (!Def)
137b60736ecSDimitry Andric     return Reg;
138b60736ecSDimitry Andric 
139b60736ecSDimitry Andric   auto DestSrc = TII.isCopyInstr(*Def);
140b60736ecSDimitry Andric   if (!DestSrc || DestSrc->Destination->getReg() != Reg)
141b60736ecSDimitry Andric     return Reg;
142b60736ecSDimitry Andric 
143b60736ecSDimitry Andric   Register SrcReg = DestSrc->Source->getReg();
144b60736ecSDimitry Andric 
145b60736ecSDimitry Andric   if (getRegisterSize(TRI, Reg) != getRegisterSize(TRI, SrcReg))
146b60736ecSDimitry Andric     return Reg;
147b60736ecSDimitry Andric 
148b60736ecSDimitry Andric   LLVM_DEBUG(dbgs() << "spillRegisters: perform copy propagation "
149b60736ecSDimitry Andric                     << printReg(Reg, &TRI) << " -> " << printReg(SrcReg, &TRI)
150b60736ecSDimitry Andric                     << "\n");
151b60736ecSDimitry Andric 
152b60736ecSDimitry Andric   // Insert spill immediately after Def
153b60736ecSDimitry Andric   RI = ++MachineBasicBlock::iterator(Def);
154b60736ecSDimitry Andric   IsKill = DestSrc->Source->isKill();
155b60736ecSDimitry Andric 
156145449b1SDimitry Andric   if (!Use) {
157b60736ecSDimitry Andric     // There are no uses of original register between COPY and STATEPOINT.
158b60736ecSDimitry Andric     // There can't be any after STATEPOINT, so we can eliminate Def.
159b60736ecSDimitry Andric     LLVM_DEBUG(dbgs() << "spillRegisters: removing dead copy " << *Def);
160b60736ecSDimitry Andric     Def->eraseFromParent();
161145449b1SDimitry Andric   } else if (IsKill) {
162145449b1SDimitry Andric     // COPY will remain in place, spill will be inserted *after* it, so it is
163145449b1SDimitry Andric     // not a kill of source anymore.
164145449b1SDimitry Andric     const_cast<MachineOperand *>(DestSrc->Source)->setIsKill(false);
165b60736ecSDimitry Andric   }
166145449b1SDimitry Andric 
167b60736ecSDimitry Andric   return SrcReg;
168b60736ecSDimitry Andric }
169b60736ecSDimitry Andric 
170cfca06d7SDimitry Andric namespace {
171b60736ecSDimitry Andric // Pair {Register, FrameIndex}
172b60736ecSDimitry Andric using RegSlotPair = std::pair<Register, int>;
173b60736ecSDimitry Andric 
174b60736ecSDimitry Andric // Keeps track of what reloads were inserted in MBB.
175b60736ecSDimitry Andric class RegReloadCache {
176b60736ecSDimitry Andric   using ReloadSet = SmallSet<RegSlotPair, 8>;
177b60736ecSDimitry Andric   DenseMap<const MachineBasicBlock *, ReloadSet> Reloads;
178b60736ecSDimitry Andric 
179b60736ecSDimitry Andric public:
180b60736ecSDimitry Andric   RegReloadCache() = default;
181b60736ecSDimitry Andric 
182b60736ecSDimitry Andric   // Record reload of Reg from FI in block MBB
recordReload(Register Reg,int FI,const MachineBasicBlock * MBB)183b60736ecSDimitry Andric   void recordReload(Register Reg, int FI, const MachineBasicBlock *MBB) {
184b60736ecSDimitry Andric     RegSlotPair RSP(Reg, FI);
185b60736ecSDimitry Andric     auto Res = Reloads[MBB].insert(RSP);
186b60736ecSDimitry Andric     (void)Res;
187b60736ecSDimitry Andric     assert(Res.second && "reload already exists");
188b60736ecSDimitry Andric   }
189b60736ecSDimitry Andric 
190b60736ecSDimitry Andric   // Does basic block MBB contains reload of Reg from FI?
hasReload(Register Reg,int FI,const MachineBasicBlock * MBB)191b60736ecSDimitry Andric   bool hasReload(Register Reg, int FI, const MachineBasicBlock *MBB) {
192b60736ecSDimitry Andric     RegSlotPair RSP(Reg, FI);
193b60736ecSDimitry Andric     return Reloads.count(MBB) && Reloads[MBB].count(RSP);
194b60736ecSDimitry Andric   }
195b60736ecSDimitry Andric };
196b60736ecSDimitry Andric 
197cfca06d7SDimitry Andric // Cache used frame indexes during statepoint re-write to re-use them in
198cfca06d7SDimitry Andric // processing next statepoint instruction.
199cfca06d7SDimitry Andric // Two strategies. One is to preserve the size of spill slot while another one
200cfca06d7SDimitry Andric // extends the size of spill slots to reduce the number of them, causing
201cfca06d7SDimitry Andric // the less total frame size. But unspill will have "implicit" any extend.
202cfca06d7SDimitry Andric class FrameIndexesCache {
203cfca06d7SDimitry Andric private:
204cfca06d7SDimitry Andric   struct FrameIndexesPerSize {
205cfca06d7SDimitry Andric     // List of used frame indexes during processing previous statepoints.
206cfca06d7SDimitry Andric     SmallVector<int, 8> Slots;
207cfca06d7SDimitry Andric     // Current index of un-used yet frame index.
208cfca06d7SDimitry Andric     unsigned Index = 0;
209cfca06d7SDimitry Andric   };
210cfca06d7SDimitry Andric   MachineFrameInfo &MFI;
211cfca06d7SDimitry Andric   const TargetRegisterInfo &TRI;
212cfca06d7SDimitry Andric   // Map size to list of frame indexes of this size. If the mode is
213cfca06d7SDimitry Andric   // FixupSCSExtendSlotSize then the key 0 is used to keep all frame indexes.
214cfca06d7SDimitry Andric   // If the size of required spill slot is greater than in a cache then the
215cfca06d7SDimitry Andric   // size will be increased.
216cfca06d7SDimitry Andric   DenseMap<unsigned, FrameIndexesPerSize> Cache;
217cfca06d7SDimitry Andric 
218b60736ecSDimitry Andric   // Keeps track of slots reserved for the shared landing pad processing.
219b60736ecSDimitry Andric   // Initialized from GlobalIndices for the current EHPad.
220b60736ecSDimitry Andric   SmallSet<int, 8> ReservedSlots;
221b60736ecSDimitry Andric 
222b60736ecSDimitry Andric   // Landing pad can be destination of several statepoints. Every register
223b60736ecSDimitry Andric   // defined by such statepoints must be spilled to the same stack slot.
224b60736ecSDimitry Andric   // This map keeps that information.
225b60736ecSDimitry Andric   DenseMap<const MachineBasicBlock *, SmallVector<RegSlotPair, 8>>
226b60736ecSDimitry Andric       GlobalIndices;
227b60736ecSDimitry Andric 
getCacheBucket(unsigned Size)228b60736ecSDimitry Andric   FrameIndexesPerSize &getCacheBucket(unsigned Size) {
229b60736ecSDimitry Andric     // In FixupSCSExtendSlotSize mode the bucket with 0 index is used
230b60736ecSDimitry Andric     // for all sizes.
231b60736ecSDimitry Andric     return Cache[FixupSCSExtendSlotSize ? 0 : Size];
232b60736ecSDimitry Andric   }
233b60736ecSDimitry Andric 
234cfca06d7SDimitry Andric public:
FrameIndexesCache(MachineFrameInfo & MFI,const TargetRegisterInfo & TRI)235cfca06d7SDimitry Andric   FrameIndexesCache(MachineFrameInfo &MFI, const TargetRegisterInfo &TRI)
236cfca06d7SDimitry Andric       : MFI(MFI), TRI(TRI) {}
237cfca06d7SDimitry Andric   // Reset the current state of used frame indexes. After invocation of
238b60736ecSDimitry Andric   // this function all frame indexes are available for allocation with
239b60736ecSDimitry Andric   // the exception of slots reserved for landing pad processing (if any).
reset(const MachineBasicBlock * EHPad)240b60736ecSDimitry Andric   void reset(const MachineBasicBlock *EHPad) {
241cfca06d7SDimitry Andric     for (auto &It : Cache)
242cfca06d7SDimitry Andric       It.second.Index = 0;
243b60736ecSDimitry Andric 
244b60736ecSDimitry Andric     ReservedSlots.clear();
245b60736ecSDimitry Andric     if (EHPad && GlobalIndices.count(EHPad))
246b60736ecSDimitry Andric       for (auto &RSP : GlobalIndices[EHPad])
247b60736ecSDimitry Andric         ReservedSlots.insert(RSP.second);
248cfca06d7SDimitry Andric   }
249b60736ecSDimitry Andric 
250cfca06d7SDimitry Andric   // Get frame index to spill the register.
getFrameIndex(Register Reg,MachineBasicBlock * EHPad)251b60736ecSDimitry Andric   int getFrameIndex(Register Reg, MachineBasicBlock *EHPad) {
252b60736ecSDimitry Andric     // Check if slot for Reg is already reserved at EHPad.
253b60736ecSDimitry Andric     auto It = GlobalIndices.find(EHPad);
254b60736ecSDimitry Andric     if (It != GlobalIndices.end()) {
255b60736ecSDimitry Andric       auto &Vec = It->second;
256b60736ecSDimitry Andric       auto Idx = llvm::find_if(
257b60736ecSDimitry Andric           Vec, [Reg](RegSlotPair &RSP) { return Reg == RSP.first; });
258b60736ecSDimitry Andric       if (Idx != Vec.end()) {
259b60736ecSDimitry Andric         int FI = Idx->second;
260b60736ecSDimitry Andric         LLVM_DEBUG(dbgs() << "Found global FI " << FI << " for register "
261b60736ecSDimitry Andric                           << printReg(Reg, &TRI) << " at "
262b60736ecSDimitry Andric                           << printMBBReference(*EHPad) << "\n");
263b60736ecSDimitry Andric         assert(ReservedSlots.count(FI) && "using unreserved slot");
264b60736ecSDimitry Andric         return FI;
265b60736ecSDimitry Andric       }
266b60736ecSDimitry Andric     }
267b60736ecSDimitry Andric 
268cfca06d7SDimitry Andric     unsigned Size = getRegisterSize(TRI, Reg);
269b60736ecSDimitry Andric     FrameIndexesPerSize &Line = getCacheBucket(Size);
270b60736ecSDimitry Andric     while (Line.Index < Line.Slots.size()) {
271cfca06d7SDimitry Andric       int FI = Line.Slots[Line.Index++];
272b60736ecSDimitry Andric       if (ReservedSlots.count(FI))
273b60736ecSDimitry Andric         continue;
274cfca06d7SDimitry Andric       // If all sizes are kept together we probably need to extend the
275cfca06d7SDimitry Andric       // spill slot size.
276cfca06d7SDimitry Andric       if (MFI.getObjectSize(FI) < Size) {
277cfca06d7SDimitry Andric         MFI.setObjectSize(FI, Size);
278cfca06d7SDimitry Andric         MFI.setObjectAlignment(FI, Align(Size));
279cfca06d7SDimitry Andric         NumSpillSlotsExtended++;
280cfca06d7SDimitry Andric       }
281cfca06d7SDimitry Andric       return FI;
282cfca06d7SDimitry Andric     }
283cfca06d7SDimitry Andric     int FI = MFI.CreateSpillStackObject(Size, Align(Size));
284cfca06d7SDimitry Andric     NumSpillSlotsAllocated++;
285cfca06d7SDimitry Andric     Line.Slots.push_back(FI);
286cfca06d7SDimitry Andric     ++Line.Index;
287b60736ecSDimitry Andric 
288b60736ecSDimitry Andric     // Remember assignment {Reg, FI} for EHPad
289b60736ecSDimitry Andric     if (EHPad) {
290b60736ecSDimitry Andric       GlobalIndices[EHPad].push_back(std::make_pair(Reg, FI));
291b60736ecSDimitry Andric       LLVM_DEBUG(dbgs() << "Reserved FI " << FI << " for spilling reg "
292b60736ecSDimitry Andric                         << printReg(Reg, &TRI) << " at landing pad "
293b60736ecSDimitry Andric                         << printMBBReference(*EHPad) << "\n");
294b60736ecSDimitry Andric     }
295b60736ecSDimitry Andric 
296cfca06d7SDimitry Andric     return FI;
297cfca06d7SDimitry Andric   }
298b60736ecSDimitry Andric 
299cfca06d7SDimitry Andric   // Sort all registers to spill in descendent order. In the
300cfca06d7SDimitry Andric   // FixupSCSExtendSlotSize mode it will minimize the total frame size.
301cfca06d7SDimitry Andric   // In non FixupSCSExtendSlotSize mode we can skip this step.
sortRegisters(SmallVectorImpl<Register> & Regs)302cfca06d7SDimitry Andric   void sortRegisters(SmallVectorImpl<Register> &Regs) {
303cfca06d7SDimitry Andric     if (!FixupSCSExtendSlotSize)
304cfca06d7SDimitry Andric       return;
305b60736ecSDimitry Andric     llvm::sort(Regs, [&](Register &A, Register &B) {
306cfca06d7SDimitry Andric       return getRegisterSize(TRI, A) > getRegisterSize(TRI, B);
307cfca06d7SDimitry Andric     });
308cfca06d7SDimitry Andric   }
309cfca06d7SDimitry Andric };
310cfca06d7SDimitry Andric 
311cfca06d7SDimitry Andric // Describes the state of the current processing statepoint instruction.
312cfca06d7SDimitry Andric class StatepointState {
313cfca06d7SDimitry Andric private:
314cfca06d7SDimitry Andric   // statepoint instruction.
315cfca06d7SDimitry Andric   MachineInstr &MI;
316cfca06d7SDimitry Andric   MachineFunction &MF;
317b60736ecSDimitry Andric   // If non-null then statepoint is invoke, and this points to the landing pad.
318b60736ecSDimitry Andric   MachineBasicBlock *EHPad;
319cfca06d7SDimitry Andric   const TargetRegisterInfo &TRI;
320cfca06d7SDimitry Andric   const TargetInstrInfo &TII;
321cfca06d7SDimitry Andric   MachineFrameInfo &MFI;
322cfca06d7SDimitry Andric   // Mask with callee saved registers.
323cfca06d7SDimitry Andric   const uint32_t *Mask;
324cfca06d7SDimitry Andric   // Cache of frame indexes used on previous instruction processing.
325cfca06d7SDimitry Andric   FrameIndexesCache &CacheFI;
326b60736ecSDimitry Andric   bool AllowGCPtrInCSR;
327cfca06d7SDimitry Andric   // Operands with physical registers requiring spilling.
328cfca06d7SDimitry Andric   SmallVector<unsigned, 8> OpsToSpill;
329cfca06d7SDimitry Andric   // Set of register to spill.
330cfca06d7SDimitry Andric   SmallVector<Register, 8> RegsToSpill;
331b60736ecSDimitry Andric   // Set of registers to reload after statepoint.
332b60736ecSDimitry Andric   SmallVector<Register, 8> RegsToReload;
333cfca06d7SDimitry Andric   // Map Register to Frame Slot index.
334cfca06d7SDimitry Andric   DenseMap<Register, int> RegToSlotIdx;
335cfca06d7SDimitry Andric 
336cfca06d7SDimitry Andric public:
StatepointState(MachineInstr & MI,const uint32_t * Mask,FrameIndexesCache & CacheFI,bool AllowGCPtrInCSR)337cfca06d7SDimitry Andric   StatepointState(MachineInstr &MI, const uint32_t *Mask,
338b60736ecSDimitry Andric                   FrameIndexesCache &CacheFI, bool AllowGCPtrInCSR)
339cfca06d7SDimitry Andric       : MI(MI), MF(*MI.getMF()), TRI(*MF.getSubtarget().getRegisterInfo()),
340cfca06d7SDimitry Andric         TII(*MF.getSubtarget().getInstrInfo()), MFI(MF.getFrameInfo()),
341b60736ecSDimitry Andric         Mask(Mask), CacheFI(CacheFI), AllowGCPtrInCSR(AllowGCPtrInCSR) {
342b60736ecSDimitry Andric 
343b60736ecSDimitry Andric     // Find statepoint's landing pad, if any.
344b60736ecSDimitry Andric     EHPad = nullptr;
345b60736ecSDimitry Andric     MachineBasicBlock *MBB = MI.getParent();
346b60736ecSDimitry Andric     // Invoke statepoint must be last one in block.
347b60736ecSDimitry Andric     bool Last = std::none_of(++MI.getIterator(), MBB->end().getInstrIterator(),
348b60736ecSDimitry Andric                              [](MachineInstr &I) {
349b60736ecSDimitry Andric                                return I.getOpcode() == TargetOpcode::STATEPOINT;
350b60736ecSDimitry Andric                              });
351b60736ecSDimitry Andric 
352b60736ecSDimitry Andric     if (!Last)
353b60736ecSDimitry Andric       return;
354b60736ecSDimitry Andric 
355b60736ecSDimitry Andric     auto IsEHPad = [](MachineBasicBlock *B) { return B->isEHPad(); };
356b60736ecSDimitry Andric 
357b60736ecSDimitry Andric     assert(llvm::count_if(MBB->successors(), IsEHPad) < 2 && "multiple EHPads");
358b60736ecSDimitry Andric 
359b60736ecSDimitry Andric     auto It = llvm::find_if(MBB->successors(), IsEHPad);
360b60736ecSDimitry Andric     if (It != MBB->succ_end())
361b60736ecSDimitry Andric       EHPad = *It;
362b60736ecSDimitry Andric   }
363b60736ecSDimitry Andric 
getEHPad() const364b60736ecSDimitry Andric   MachineBasicBlock *getEHPad() const { return EHPad; }
365b60736ecSDimitry Andric 
366cfca06d7SDimitry Andric   // Return true if register is callee saved.
isCalleeSaved(Register Reg)367cfca06d7SDimitry Andric   bool isCalleeSaved(Register Reg) { return (Mask[Reg / 32] >> Reg % 32) & 1; }
368b60736ecSDimitry Andric 
369cfca06d7SDimitry Andric   // Iterates over statepoint meta args to find caller saver registers.
370cfca06d7SDimitry Andric   // Also cache the size of found registers.
371cfca06d7SDimitry Andric   // Returns true if caller save registers found.
findRegistersToSpill()372cfca06d7SDimitry Andric   bool findRegistersToSpill() {
373b60736ecSDimitry Andric     SmallSet<Register, 8> GCRegs;
374b60736ecSDimitry Andric     // All GC pointer operands assigned to registers produce new value.
375b60736ecSDimitry Andric     // Since they're tied to their defs, it is enough to collect def registers.
376b60736ecSDimitry Andric     for (const auto &Def : MI.defs())
377b60736ecSDimitry Andric       GCRegs.insert(Def.getReg());
378b60736ecSDimitry Andric 
379cfca06d7SDimitry Andric     SmallSet<Register, 8> VisitedRegs;
380cfca06d7SDimitry Andric     for (unsigned Idx = StatepointOpers(&MI).getVarIdx(),
381cfca06d7SDimitry Andric                   EndIdx = MI.getNumOperands();
382cfca06d7SDimitry Andric          Idx < EndIdx; ++Idx) {
383cfca06d7SDimitry Andric       MachineOperand &MO = MI.getOperand(Idx);
384b60736ecSDimitry Andric       // Leave `undef` operands as is, StackMaps will rewrite them
385b60736ecSDimitry Andric       // into a constant.
386b60736ecSDimitry Andric       if (!MO.isReg() || MO.isImplicit() || MO.isUndef())
387cfca06d7SDimitry Andric         continue;
388cfca06d7SDimitry Andric       Register Reg = MO.getReg();
389cfca06d7SDimitry Andric       assert(Reg.isPhysical() && "Only physical regs are expected");
390b60736ecSDimitry Andric 
3917fa27ce4SDimitry Andric       if (isCalleeSaved(Reg) && (AllowGCPtrInCSR || !GCRegs.contains(Reg)))
392cfca06d7SDimitry Andric         continue;
393b60736ecSDimitry Andric 
394b60736ecSDimitry Andric       LLVM_DEBUG(dbgs() << "Will spill " << printReg(Reg, &TRI) << " at index "
395b60736ecSDimitry Andric                         << Idx << "\n");
396b60736ecSDimitry Andric 
397cfca06d7SDimitry Andric       if (VisitedRegs.insert(Reg).second)
398cfca06d7SDimitry Andric         RegsToSpill.push_back(Reg);
399cfca06d7SDimitry Andric       OpsToSpill.push_back(Idx);
400cfca06d7SDimitry Andric     }
401cfca06d7SDimitry Andric     CacheFI.sortRegisters(RegsToSpill);
402cfca06d7SDimitry Andric     return !RegsToSpill.empty();
403cfca06d7SDimitry Andric   }
404b60736ecSDimitry Andric 
405cfca06d7SDimitry Andric   // Spill all caller saved registers right before statepoint instruction.
406cfca06d7SDimitry Andric   // Remember frame index where register is spilled.
spillRegisters()407cfca06d7SDimitry Andric   void spillRegisters() {
408cfca06d7SDimitry Andric     for (Register Reg : RegsToSpill) {
409b60736ecSDimitry Andric       int FI = CacheFI.getFrameIndex(Reg, EHPad);
410b60736ecSDimitry Andric 
411cfca06d7SDimitry Andric       NumSpilledRegisters++;
412cfca06d7SDimitry Andric       RegToSlotIdx[Reg] = FI;
413b60736ecSDimitry Andric 
414b60736ecSDimitry Andric       LLVM_DEBUG(dbgs() << "Spilling " << printReg(Reg, &TRI) << " to FI " << FI
415b60736ecSDimitry Andric                         << "\n");
416b60736ecSDimitry Andric 
417b60736ecSDimitry Andric       // Perform trivial copy propagation
418b60736ecSDimitry Andric       bool IsKill = true;
419b60736ecSDimitry Andric       MachineBasicBlock::iterator InsertBefore(MI);
420b60736ecSDimitry Andric       Reg = performCopyPropagation(Reg, InsertBefore, IsKill, TII, TRI);
4217fa27ce4SDimitry Andric       const TargetRegisterClass *RC = TRI.getMinimalPhysRegClass(Reg);
422b60736ecSDimitry Andric 
423b60736ecSDimitry Andric       LLVM_DEBUG(dbgs() << "Insert spill before " << *InsertBefore);
424b60736ecSDimitry Andric       TII.storeRegToStackSlot(*MI.getParent(), InsertBefore, Reg, IsKill, FI,
425e3b55780SDimitry Andric                               RC, &TRI, Register());
426cfca06d7SDimitry Andric     }
427cfca06d7SDimitry Andric   }
428b60736ecSDimitry Andric 
insertReloadBefore(unsigned Reg,MachineBasicBlock::iterator It,MachineBasicBlock * MBB)429b60736ecSDimitry Andric   void insertReloadBefore(unsigned Reg, MachineBasicBlock::iterator It,
430b60736ecSDimitry Andric                           MachineBasicBlock *MBB) {
431b60736ecSDimitry Andric     const TargetRegisterClass *RC = TRI.getMinimalPhysRegClass(Reg);
432b60736ecSDimitry Andric     int FI = RegToSlotIdx[Reg];
433b60736ecSDimitry Andric     if (It != MBB->end()) {
434e3b55780SDimitry Andric       TII.loadRegFromStackSlot(*MBB, It, Reg, FI, RC, &TRI, Register());
435b60736ecSDimitry Andric       return;
436b60736ecSDimitry Andric     }
437b60736ecSDimitry Andric 
438b60736ecSDimitry Andric     // To insert reload at the end of MBB, insert it before last instruction
439b60736ecSDimitry Andric     // and then swap them.
440b60736ecSDimitry Andric     assert(!MBB->empty() && "Empty block");
441b60736ecSDimitry Andric     --It;
442e3b55780SDimitry Andric     TII.loadRegFromStackSlot(*MBB, It, Reg, FI, RC, &TRI, Register());
443b60736ecSDimitry Andric     MachineInstr *Reload = It->getPrevNode();
444b60736ecSDimitry Andric     int Dummy = 0;
445b60736ecSDimitry Andric     (void)Dummy;
446b60736ecSDimitry Andric     assert(TII.isLoadFromStackSlot(*Reload, Dummy) == Reg);
447b60736ecSDimitry Andric     assert(Dummy == FI);
448b60736ecSDimitry Andric     MBB->remove(Reload);
449b60736ecSDimitry Andric     MBB->insertAfter(It, Reload);
450b60736ecSDimitry Andric   }
451b60736ecSDimitry Andric 
452b60736ecSDimitry Andric   // Insert reloads of (relocated) registers spilled in statepoint.
insertReloads(MachineInstr * NewStatepoint,RegReloadCache & RC)453b60736ecSDimitry Andric   void insertReloads(MachineInstr *NewStatepoint, RegReloadCache &RC) {
454b60736ecSDimitry Andric     MachineBasicBlock *MBB = NewStatepoint->getParent();
455b60736ecSDimitry Andric     auto InsertPoint = std::next(NewStatepoint->getIterator());
456b60736ecSDimitry Andric 
457b60736ecSDimitry Andric     for (auto Reg : RegsToReload) {
458b60736ecSDimitry Andric       insertReloadBefore(Reg, InsertPoint, MBB);
459b60736ecSDimitry Andric       LLVM_DEBUG(dbgs() << "Reloading " << printReg(Reg, &TRI) << " from FI "
460b60736ecSDimitry Andric                         << RegToSlotIdx[Reg] << " after statepoint\n");
461b60736ecSDimitry Andric 
462b60736ecSDimitry Andric       if (EHPad && !RC.hasReload(Reg, RegToSlotIdx[Reg], EHPad)) {
463b60736ecSDimitry Andric         RC.recordReload(Reg, RegToSlotIdx[Reg], EHPad);
464b1c73532SDimitry Andric         auto EHPadInsertPoint =
465b1c73532SDimitry Andric             EHPad->SkipPHIsLabelsAndDebug(EHPad->begin(), Reg);
466b60736ecSDimitry Andric         insertReloadBefore(Reg, EHPadInsertPoint, EHPad);
467b60736ecSDimitry Andric         LLVM_DEBUG(dbgs() << "...also reload at EHPad "
468b60736ecSDimitry Andric                           << printMBBReference(*EHPad) << "\n");
469b60736ecSDimitry Andric       }
470b60736ecSDimitry Andric     }
471b60736ecSDimitry Andric   }
472b60736ecSDimitry Andric 
473cfca06d7SDimitry Andric   // Re-write statepoint machine instruction to replace caller saved operands
474cfca06d7SDimitry Andric   // with indirect memory location (frame index).
rewriteStatepoint()475b60736ecSDimitry Andric   MachineInstr *rewriteStatepoint() {
476cfca06d7SDimitry Andric     MachineInstr *NewMI =
477cfca06d7SDimitry Andric         MF.CreateMachineInstr(TII.get(MI.getOpcode()), MI.getDebugLoc(), true);
478cfca06d7SDimitry Andric     MachineInstrBuilder MIB(MF, NewMI);
479cfca06d7SDimitry Andric 
480b60736ecSDimitry Andric     unsigned NumOps = MI.getNumOperands();
481b60736ecSDimitry Andric 
482b60736ecSDimitry Andric     // New indices for the remaining defs.
483b60736ecSDimitry Andric     SmallVector<unsigned, 8> NewIndices;
484b60736ecSDimitry Andric     unsigned NumDefs = MI.getNumDefs();
485b60736ecSDimitry Andric     for (unsigned I = 0; I < NumDefs; ++I) {
486b60736ecSDimitry Andric       MachineOperand &DefMO = MI.getOperand(I);
487b60736ecSDimitry Andric       assert(DefMO.isReg() && DefMO.isDef() && "Expected Reg Def operand");
488b60736ecSDimitry Andric       Register Reg = DefMO.getReg();
489344a3780SDimitry Andric       assert(DefMO.isTied() && "Def is expected to be tied");
490344a3780SDimitry Andric       // We skipped undef uses and did not spill them, so we should not
491344a3780SDimitry Andric       // proceed with defs here.
492344a3780SDimitry Andric       if (MI.getOperand(MI.findTiedOperandIdx(I)).isUndef()) {
493344a3780SDimitry Andric         if (AllowGCPtrInCSR) {
494344a3780SDimitry Andric           NewIndices.push_back(NewMI->getNumOperands());
495344a3780SDimitry Andric           MIB.addReg(Reg, RegState::Define);
496344a3780SDimitry Andric         }
497344a3780SDimitry Andric         continue;
498344a3780SDimitry Andric       }
499b60736ecSDimitry Andric       if (!AllowGCPtrInCSR) {
500b60736ecSDimitry Andric         assert(is_contained(RegsToSpill, Reg));
501b60736ecSDimitry Andric         RegsToReload.push_back(Reg);
502b60736ecSDimitry Andric       } else {
503b60736ecSDimitry Andric         if (isCalleeSaved(Reg)) {
504b60736ecSDimitry Andric           NewIndices.push_back(NewMI->getNumOperands());
505b60736ecSDimitry Andric           MIB.addReg(Reg, RegState::Define);
506b60736ecSDimitry Andric         } else {
507b60736ecSDimitry Andric           NewIndices.push_back(NumOps);
508b60736ecSDimitry Andric           RegsToReload.push_back(Reg);
509b60736ecSDimitry Andric         }
510b60736ecSDimitry Andric       }
511b60736ecSDimitry Andric     }
512b60736ecSDimitry Andric 
513cfca06d7SDimitry Andric     // Add End marker.
514cfca06d7SDimitry Andric     OpsToSpill.push_back(MI.getNumOperands());
515cfca06d7SDimitry Andric     unsigned CurOpIdx = 0;
516cfca06d7SDimitry Andric 
517b60736ecSDimitry Andric     for (unsigned I = NumDefs; I < MI.getNumOperands(); ++I) {
518cfca06d7SDimitry Andric       MachineOperand &MO = MI.getOperand(I);
519cfca06d7SDimitry Andric       if (I == OpsToSpill[CurOpIdx]) {
520cfca06d7SDimitry Andric         int FI = RegToSlotIdx[MO.getReg()];
521cfca06d7SDimitry Andric         MIB.addImm(StackMaps::IndirectMemRefOp);
522cfca06d7SDimitry Andric         MIB.addImm(getRegisterSize(TRI, MO.getReg()));
523cfca06d7SDimitry Andric         assert(MO.isReg() && "Should be register");
524cfca06d7SDimitry Andric         assert(MO.getReg().isPhysical() && "Should be physical register");
525cfca06d7SDimitry Andric         MIB.addFrameIndex(FI);
526cfca06d7SDimitry Andric         MIB.addImm(0);
527cfca06d7SDimitry Andric         ++CurOpIdx;
528b60736ecSDimitry Andric       } else {
529cfca06d7SDimitry Andric         MIB.add(MO);
530b60736ecSDimitry Andric         unsigned OldDef;
531b60736ecSDimitry Andric         if (AllowGCPtrInCSR && MI.isRegTiedToDefOperand(I, &OldDef)) {
532b60736ecSDimitry Andric           assert(OldDef < NumDefs);
533b60736ecSDimitry Andric           assert(NewIndices[OldDef] < NumOps);
534b60736ecSDimitry Andric           MIB->tieOperands(NewIndices[OldDef], MIB->getNumOperands() - 1);
535b60736ecSDimitry Andric         }
536b60736ecSDimitry Andric       }
537cfca06d7SDimitry Andric     }
538cfca06d7SDimitry Andric     assert(CurOpIdx == (OpsToSpill.size() - 1) && "Not all operands processed");
539cfca06d7SDimitry Andric     // Add mem operands.
540cfca06d7SDimitry Andric     NewMI->setMemRefs(MF, MI.memoperands());
541cfca06d7SDimitry Andric     for (auto It : RegToSlotIdx) {
542b60736ecSDimitry Andric       Register R = It.first;
543cfca06d7SDimitry Andric       int FrameIndex = It.second;
544cfca06d7SDimitry Andric       auto PtrInfo = MachinePointerInfo::getFixedStack(MF, FrameIndex);
545b60736ecSDimitry Andric       MachineMemOperand::Flags Flags = MachineMemOperand::MOLoad;
546b60736ecSDimitry Andric       if (is_contained(RegsToReload, R))
547b60736ecSDimitry Andric         Flags |= MachineMemOperand::MOStore;
548b60736ecSDimitry Andric       auto *MMO =
549b60736ecSDimitry Andric           MF.getMachineMemOperand(PtrInfo, Flags, getRegisterSize(TRI, R),
550cfca06d7SDimitry Andric                                   MFI.getObjectAlign(FrameIndex));
551cfca06d7SDimitry Andric       NewMI->addMemOperand(MF, MMO);
552cfca06d7SDimitry Andric     }
553b60736ecSDimitry Andric 
554cfca06d7SDimitry Andric     // Insert new statepoint and erase old one.
555cfca06d7SDimitry Andric     MI.getParent()->insert(MI, NewMI);
556b60736ecSDimitry Andric 
557b60736ecSDimitry Andric     LLVM_DEBUG(dbgs() << "rewritten statepoint to : " << *NewMI << "\n");
558cfca06d7SDimitry Andric     MI.eraseFromParent();
559b60736ecSDimitry Andric     return NewMI;
560cfca06d7SDimitry Andric   }
561cfca06d7SDimitry Andric };
562cfca06d7SDimitry Andric 
563cfca06d7SDimitry Andric class StatepointProcessor {
564cfca06d7SDimitry Andric private:
565cfca06d7SDimitry Andric   MachineFunction &MF;
566cfca06d7SDimitry Andric   const TargetRegisterInfo &TRI;
567cfca06d7SDimitry Andric   FrameIndexesCache CacheFI;
568b60736ecSDimitry Andric   RegReloadCache ReloadCache;
569cfca06d7SDimitry Andric 
570cfca06d7SDimitry Andric public:
StatepointProcessor(MachineFunction & MF)571cfca06d7SDimitry Andric   StatepointProcessor(MachineFunction &MF)
572cfca06d7SDimitry Andric       : MF(MF), TRI(*MF.getSubtarget().getRegisterInfo()),
573cfca06d7SDimitry Andric         CacheFI(MF.getFrameInfo(), TRI) {}
574cfca06d7SDimitry Andric 
process(MachineInstr & MI,bool AllowGCPtrInCSR)575b60736ecSDimitry Andric   bool process(MachineInstr &MI, bool AllowGCPtrInCSR) {
576cfca06d7SDimitry Andric     StatepointOpers SO(&MI);
577cfca06d7SDimitry Andric     uint64_t Flags = SO.getFlags();
578cfca06d7SDimitry Andric     // Do nothing for LiveIn, it supports all registers.
579cfca06d7SDimitry Andric     if (Flags & (uint64_t)StatepointFlags::DeoptLiveIn)
580cfca06d7SDimitry Andric       return false;
581b60736ecSDimitry Andric     LLVM_DEBUG(dbgs() << "\nMBB " << MI.getParent()->getNumber() << " "
582b60736ecSDimitry Andric                       << MI.getParent()->getName() << " : process statepoint "
583b60736ecSDimitry Andric                       << MI);
584cfca06d7SDimitry Andric     CallingConv::ID CC = SO.getCallingConv();
585cfca06d7SDimitry Andric     const uint32_t *Mask = TRI.getCallPreservedMask(MF, CC);
586b60736ecSDimitry Andric     StatepointState SS(MI, Mask, CacheFI, AllowGCPtrInCSR);
587b60736ecSDimitry Andric     CacheFI.reset(SS.getEHPad());
588cfca06d7SDimitry Andric 
589cfca06d7SDimitry Andric     if (!SS.findRegistersToSpill())
590cfca06d7SDimitry Andric       return false;
591cfca06d7SDimitry Andric 
592cfca06d7SDimitry Andric     SS.spillRegisters();
593b60736ecSDimitry Andric     auto *NewStatepoint = SS.rewriteStatepoint();
594b60736ecSDimitry Andric     SS.insertReloads(NewStatepoint, ReloadCache);
595cfca06d7SDimitry Andric     return true;
596cfca06d7SDimitry Andric   }
597cfca06d7SDimitry Andric };
598cfca06d7SDimitry Andric } // namespace
599cfca06d7SDimitry Andric 
runOnMachineFunction(MachineFunction & MF)600cfca06d7SDimitry Andric bool FixupStatepointCallerSaved::runOnMachineFunction(MachineFunction &MF) {
601cfca06d7SDimitry Andric   if (skipFunction(MF.getFunction()))
602cfca06d7SDimitry Andric     return false;
603cfca06d7SDimitry Andric 
604cfca06d7SDimitry Andric   const Function &F = MF.getFunction();
605cfca06d7SDimitry Andric   if (!F.hasGC())
606cfca06d7SDimitry Andric     return false;
607cfca06d7SDimitry Andric 
608cfca06d7SDimitry Andric   SmallVector<MachineInstr *, 16> Statepoints;
609cfca06d7SDimitry Andric   for (MachineBasicBlock &BB : MF)
610cfca06d7SDimitry Andric     for (MachineInstr &I : BB)
611cfca06d7SDimitry Andric       if (I.getOpcode() == TargetOpcode::STATEPOINT)
612cfca06d7SDimitry Andric         Statepoints.push_back(&I);
613cfca06d7SDimitry Andric 
614cfca06d7SDimitry Andric   if (Statepoints.empty())
615cfca06d7SDimitry Andric     return false;
616cfca06d7SDimitry Andric 
617cfca06d7SDimitry Andric   bool Changed = false;
618cfca06d7SDimitry Andric   StatepointProcessor SPP(MF);
619b60736ecSDimitry Andric   unsigned NumStatepoints = 0;
620b60736ecSDimitry Andric   bool AllowGCPtrInCSR = PassGCPtrInCSR;
621b60736ecSDimitry Andric   for (MachineInstr *I : Statepoints) {
622b60736ecSDimitry Andric     ++NumStatepoints;
623b60736ecSDimitry Andric     if (MaxStatepointsWithRegs.getNumOccurrences() &&
624b60736ecSDimitry Andric         NumStatepoints >= MaxStatepointsWithRegs)
625b60736ecSDimitry Andric       AllowGCPtrInCSR = false;
626b60736ecSDimitry Andric     Changed |= SPP.process(*I, AllowGCPtrInCSR);
627b60736ecSDimitry Andric   }
628cfca06d7SDimitry Andric   return Changed;
629cfca06d7SDimitry Andric }
630