Path: blob/main/contrib/llvm-project/llvm/lib/CodeGen/FixupStatepointCallerSaved.cpp
35233 views
//===-- FixupStatepointCallerSaved.cpp - Fixup caller saved registers ----===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// Statepoint instruction in deopt parameters contains values which are10/// meaningful to the runtime and should be able to be read at the moment the11/// call returns. So we can say that we need to encode the fact that these12/// values are "late read" by runtime. If we could express this notion for13/// register allocator it would produce the right form for us.14/// The need to fixup (i.e this pass) is specifically handling the fact that15/// we cannot describe such a late read for the register allocator.16/// Register allocator may put the value on a register clobbered by the call.17/// This pass forces the spill of such registers and replaces corresponding18/// statepoint operands to added spill slots.19///20//===----------------------------------------------------------------------===//2122#include "llvm/ADT/SmallSet.h"23#include "llvm/ADT/Statistic.h"24#include "llvm/CodeGen/MachineFrameInfo.h"25#include "llvm/CodeGen/MachineFunctionPass.h"26#include "llvm/CodeGen/StackMaps.h"27#include "llvm/CodeGen/TargetInstrInfo.h"28#include "llvm/IR/Statepoint.h"29#include "llvm/InitializePasses.h"30#include "llvm/Support/Debug.h"3132using namespace llvm;3334#define DEBUG_TYPE "fixup-statepoint-caller-saved"35STATISTIC(NumSpilledRegisters, "Number of spilled register");36STATISTIC(NumSpillSlotsAllocated, "Number of spill slots allocated");37STATISTIC(NumSpillSlotsExtended, "Number of spill slots extended");3839static cl::opt<bool> FixupSCSExtendSlotSize(40"fixup-scs-extend-slot-size", cl::Hidden, cl::init(false),41cl::desc("Allow spill in spill slot of greater size than register size"),42cl::Hidden);4344static cl::opt<bool> PassGCPtrInCSR(45"fixup-allow-gcptr-in-csr", cl::Hidden, cl::init(false),46cl::desc("Allow passing GC Pointer arguments in callee saved registers"));4748static cl::opt<bool> EnableCopyProp(49"fixup-scs-enable-copy-propagation", cl::Hidden, cl::init(true),50cl::desc("Enable simple copy propagation during register reloading"));5152// This is purely debugging option.53// It may be handy for investigating statepoint spilling issues.54static cl::opt<unsigned> MaxStatepointsWithRegs(55"fixup-max-csr-statepoints", cl::Hidden,56cl::desc("Max number of statepoints allowed to pass GC Ptrs in registers"));5758namespace {5960class FixupStatepointCallerSaved : public MachineFunctionPass {61public:62static char ID;6364FixupStatepointCallerSaved() : MachineFunctionPass(ID) {65initializeFixupStatepointCallerSavedPass(*PassRegistry::getPassRegistry());66}6768void getAnalysisUsage(AnalysisUsage &AU) const override {69AU.setPreservesCFG();70MachineFunctionPass::getAnalysisUsage(AU);71}7273StringRef getPassName() const override {74return "Fixup Statepoint Caller Saved";75}7677bool runOnMachineFunction(MachineFunction &MF) override;78};7980} // End anonymous namespace.8182char FixupStatepointCallerSaved::ID = 0;83char &llvm::FixupStatepointCallerSavedID = FixupStatepointCallerSaved::ID;8485INITIALIZE_PASS_BEGIN(FixupStatepointCallerSaved, DEBUG_TYPE,86"Fixup Statepoint Caller Saved", false, false)87INITIALIZE_PASS_END(FixupStatepointCallerSaved, DEBUG_TYPE,88"Fixup Statepoint Caller Saved", false, false)8990// Utility function to get size of the register.91static unsigned getRegisterSize(const TargetRegisterInfo &TRI, Register Reg) {92const TargetRegisterClass *RC = TRI.getMinimalPhysRegClass(Reg);93return TRI.getSpillSize(*RC);94}9596// Try to eliminate redundant copy to register which we're going to97// spill, i.e. try to change:98// X = COPY Y99// SPILL X100// to101// SPILL Y102// If there are no uses of X between copy and STATEPOINT, that COPY103// may be eliminated.104// Reg - register we're about to spill105// RI - On entry points to statepoint.106// On successful copy propagation set to new spill point.107// IsKill - set to true if COPY is Kill (there are no uses of Y)108// Returns either found source copy register or original one.109static Register performCopyPropagation(Register Reg,110MachineBasicBlock::iterator &RI,111bool &IsKill, const TargetInstrInfo &TII,112const TargetRegisterInfo &TRI) {113// First check if statepoint itself uses Reg in non-meta operands.114int Idx = RI->findRegisterUseOperandIdx(Reg, &TRI, false);115if (Idx >= 0 && (unsigned)Idx < StatepointOpers(&*RI).getNumDeoptArgsIdx()) {116IsKill = false;117return Reg;118}119120if (!EnableCopyProp)121return Reg;122123MachineBasicBlock *MBB = RI->getParent();124MachineBasicBlock::reverse_iterator E = MBB->rend();125MachineInstr *Def = nullptr, *Use = nullptr;126for (auto It = ++(RI.getReverse()); It != E; ++It) {127if (It->readsRegister(Reg, &TRI) && !Use)128Use = &*It;129if (It->modifiesRegister(Reg, &TRI)) {130Def = &*It;131break;132}133}134135if (!Def)136return Reg;137138auto DestSrc = TII.isCopyInstr(*Def);139if (!DestSrc || DestSrc->Destination->getReg() != Reg)140return Reg;141142Register SrcReg = DestSrc->Source->getReg();143144if (getRegisterSize(TRI, Reg) != getRegisterSize(TRI, SrcReg))145return Reg;146147LLVM_DEBUG(dbgs() << "spillRegisters: perform copy propagation "148<< printReg(Reg, &TRI) << " -> " << printReg(SrcReg, &TRI)149<< "\n");150151// Insert spill immediately after Def152RI = ++MachineBasicBlock::iterator(Def);153IsKill = DestSrc->Source->isKill();154155if (!Use) {156// There are no uses of original register between COPY and STATEPOINT.157// There can't be any after STATEPOINT, so we can eliminate Def.158LLVM_DEBUG(dbgs() << "spillRegisters: removing dead copy " << *Def);159Def->eraseFromParent();160} else if (IsKill) {161// COPY will remain in place, spill will be inserted *after* it, so it is162// not a kill of source anymore.163const_cast<MachineOperand *>(DestSrc->Source)->setIsKill(false);164}165166return SrcReg;167}168169namespace {170// Pair {Register, FrameIndex}171using RegSlotPair = std::pair<Register, int>;172173// Keeps track of what reloads were inserted in MBB.174class RegReloadCache {175using ReloadSet = SmallSet<RegSlotPair, 8>;176DenseMap<const MachineBasicBlock *, ReloadSet> Reloads;177178public:179RegReloadCache() = default;180181// Record reload of Reg from FI in block MBB182void recordReload(Register Reg, int FI, const MachineBasicBlock *MBB) {183RegSlotPair RSP(Reg, FI);184auto Res = Reloads[MBB].insert(RSP);185(void)Res;186assert(Res.second && "reload already exists");187}188189// Does basic block MBB contains reload of Reg from FI?190bool hasReload(Register Reg, int FI, const MachineBasicBlock *MBB) {191RegSlotPair RSP(Reg, FI);192return Reloads.count(MBB) && Reloads[MBB].count(RSP);193}194};195196// Cache used frame indexes during statepoint re-write to re-use them in197// processing next statepoint instruction.198// Two strategies. One is to preserve the size of spill slot while another one199// extends the size of spill slots to reduce the number of them, causing200// the less total frame size. But unspill will have "implicit" any extend.201class FrameIndexesCache {202private:203struct FrameIndexesPerSize {204// List of used frame indexes during processing previous statepoints.205SmallVector<int, 8> Slots;206// Current index of un-used yet frame index.207unsigned Index = 0;208};209MachineFrameInfo &MFI;210const TargetRegisterInfo &TRI;211// Map size to list of frame indexes of this size. If the mode is212// FixupSCSExtendSlotSize then the key 0 is used to keep all frame indexes.213// If the size of required spill slot is greater than in a cache then the214// size will be increased.215DenseMap<unsigned, FrameIndexesPerSize> Cache;216217// Keeps track of slots reserved for the shared landing pad processing.218// Initialized from GlobalIndices for the current EHPad.219SmallSet<int, 8> ReservedSlots;220221// Landing pad can be destination of several statepoints. Every register222// defined by such statepoints must be spilled to the same stack slot.223// This map keeps that information.224DenseMap<const MachineBasicBlock *, SmallVector<RegSlotPair, 8>>225GlobalIndices;226227FrameIndexesPerSize &getCacheBucket(unsigned Size) {228// In FixupSCSExtendSlotSize mode the bucket with 0 index is used229// for all sizes.230return Cache[FixupSCSExtendSlotSize ? 0 : Size];231}232233public:234FrameIndexesCache(MachineFrameInfo &MFI, const TargetRegisterInfo &TRI)235: MFI(MFI), TRI(TRI) {}236// Reset the current state of used frame indexes. After invocation of237// this function all frame indexes are available for allocation with238// the exception of slots reserved for landing pad processing (if any).239void reset(const MachineBasicBlock *EHPad) {240for (auto &It : Cache)241It.second.Index = 0;242243ReservedSlots.clear();244if (EHPad && GlobalIndices.count(EHPad))245for (auto &RSP : GlobalIndices[EHPad])246ReservedSlots.insert(RSP.second);247}248249// Get frame index to spill the register.250int getFrameIndex(Register Reg, MachineBasicBlock *EHPad) {251// Check if slot for Reg is already reserved at EHPad.252auto It = GlobalIndices.find(EHPad);253if (It != GlobalIndices.end()) {254auto &Vec = It->second;255auto Idx = llvm::find_if(256Vec, [Reg](RegSlotPair &RSP) { return Reg == RSP.first; });257if (Idx != Vec.end()) {258int FI = Idx->second;259LLVM_DEBUG(dbgs() << "Found global FI " << FI << " for register "260<< printReg(Reg, &TRI) << " at "261<< printMBBReference(*EHPad) << "\n");262assert(ReservedSlots.count(FI) && "using unreserved slot");263return FI;264}265}266267unsigned Size = getRegisterSize(TRI, Reg);268FrameIndexesPerSize &Line = getCacheBucket(Size);269while (Line.Index < Line.Slots.size()) {270int FI = Line.Slots[Line.Index++];271if (ReservedSlots.count(FI))272continue;273// If all sizes are kept together we probably need to extend the274// spill slot size.275if (MFI.getObjectSize(FI) < Size) {276MFI.setObjectSize(FI, Size);277MFI.setObjectAlignment(FI, Align(Size));278NumSpillSlotsExtended++;279}280return FI;281}282int FI = MFI.CreateSpillStackObject(Size, Align(Size));283NumSpillSlotsAllocated++;284Line.Slots.push_back(FI);285++Line.Index;286287// Remember assignment {Reg, FI} for EHPad288if (EHPad) {289GlobalIndices[EHPad].push_back(std::make_pair(Reg, FI));290LLVM_DEBUG(dbgs() << "Reserved FI " << FI << " for spilling reg "291<< printReg(Reg, &TRI) << " at landing pad "292<< printMBBReference(*EHPad) << "\n");293}294295return FI;296}297298// Sort all registers to spill in descendent order. In the299// FixupSCSExtendSlotSize mode it will minimize the total frame size.300// In non FixupSCSExtendSlotSize mode we can skip this step.301void sortRegisters(SmallVectorImpl<Register> &Regs) {302if (!FixupSCSExtendSlotSize)303return;304llvm::sort(Regs, [&](Register &A, Register &B) {305return getRegisterSize(TRI, A) > getRegisterSize(TRI, B);306});307}308};309310// Describes the state of the current processing statepoint instruction.311class StatepointState {312private:313// statepoint instruction.314MachineInstr &MI;315MachineFunction &MF;316// If non-null then statepoint is invoke, and this points to the landing pad.317MachineBasicBlock *EHPad;318const TargetRegisterInfo &TRI;319const TargetInstrInfo &TII;320MachineFrameInfo &MFI;321// Mask with callee saved registers.322const uint32_t *Mask;323// Cache of frame indexes used on previous instruction processing.324FrameIndexesCache &CacheFI;325bool AllowGCPtrInCSR;326// Operands with physical registers requiring spilling.327SmallVector<unsigned, 8> OpsToSpill;328// Set of register to spill.329SmallVector<Register, 8> RegsToSpill;330// Set of registers to reload after statepoint.331SmallVector<Register, 8> RegsToReload;332// Map Register to Frame Slot index.333DenseMap<Register, int> RegToSlotIdx;334335public:336StatepointState(MachineInstr &MI, const uint32_t *Mask,337FrameIndexesCache &CacheFI, bool AllowGCPtrInCSR)338: MI(MI), MF(*MI.getMF()), TRI(*MF.getSubtarget().getRegisterInfo()),339TII(*MF.getSubtarget().getInstrInfo()), MFI(MF.getFrameInfo()),340Mask(Mask), CacheFI(CacheFI), AllowGCPtrInCSR(AllowGCPtrInCSR) {341342// Find statepoint's landing pad, if any.343EHPad = nullptr;344MachineBasicBlock *MBB = MI.getParent();345// Invoke statepoint must be last one in block.346bool Last = std::none_of(++MI.getIterator(), MBB->end().getInstrIterator(),347[](MachineInstr &I) {348return I.getOpcode() == TargetOpcode::STATEPOINT;349});350351if (!Last)352return;353354auto IsEHPad = [](MachineBasicBlock *B) { return B->isEHPad(); };355356assert(llvm::count_if(MBB->successors(), IsEHPad) < 2 && "multiple EHPads");357358auto It = llvm::find_if(MBB->successors(), IsEHPad);359if (It != MBB->succ_end())360EHPad = *It;361}362363MachineBasicBlock *getEHPad() const { return EHPad; }364365// Return true if register is callee saved.366bool isCalleeSaved(Register Reg) { return (Mask[Reg / 32] >> Reg % 32) & 1; }367368// Iterates over statepoint meta args to find caller saver registers.369// Also cache the size of found registers.370// Returns true if caller save registers found.371bool findRegistersToSpill() {372SmallSet<Register, 8> GCRegs;373// All GC pointer operands assigned to registers produce new value.374// Since they're tied to their defs, it is enough to collect def registers.375for (const auto &Def : MI.defs())376GCRegs.insert(Def.getReg());377378SmallSet<Register, 8> VisitedRegs;379for (unsigned Idx = StatepointOpers(&MI).getVarIdx(),380EndIdx = MI.getNumOperands();381Idx < EndIdx; ++Idx) {382MachineOperand &MO = MI.getOperand(Idx);383// Leave `undef` operands as is, StackMaps will rewrite them384// into a constant.385if (!MO.isReg() || MO.isImplicit() || MO.isUndef())386continue;387Register Reg = MO.getReg();388assert(Reg.isPhysical() && "Only physical regs are expected");389390if (isCalleeSaved(Reg) && (AllowGCPtrInCSR || !GCRegs.contains(Reg)))391continue;392393LLVM_DEBUG(dbgs() << "Will spill " << printReg(Reg, &TRI) << " at index "394<< Idx << "\n");395396if (VisitedRegs.insert(Reg).second)397RegsToSpill.push_back(Reg);398OpsToSpill.push_back(Idx);399}400CacheFI.sortRegisters(RegsToSpill);401return !RegsToSpill.empty();402}403404// Spill all caller saved registers right before statepoint instruction.405// Remember frame index where register is spilled.406void spillRegisters() {407for (Register Reg : RegsToSpill) {408int FI = CacheFI.getFrameIndex(Reg, EHPad);409410NumSpilledRegisters++;411RegToSlotIdx[Reg] = FI;412413LLVM_DEBUG(dbgs() << "Spilling " << printReg(Reg, &TRI) << " to FI " << FI414<< "\n");415416// Perform trivial copy propagation417bool IsKill = true;418MachineBasicBlock::iterator InsertBefore(MI);419Reg = performCopyPropagation(Reg, InsertBefore, IsKill, TII, TRI);420const TargetRegisterClass *RC = TRI.getMinimalPhysRegClass(Reg);421422LLVM_DEBUG(dbgs() << "Insert spill before " << *InsertBefore);423TII.storeRegToStackSlot(*MI.getParent(), InsertBefore, Reg, IsKill, FI,424RC, &TRI, Register());425}426}427428void insertReloadBefore(unsigned Reg, MachineBasicBlock::iterator It,429MachineBasicBlock *MBB) {430const TargetRegisterClass *RC = TRI.getMinimalPhysRegClass(Reg);431int FI = RegToSlotIdx[Reg];432if (It != MBB->end()) {433TII.loadRegFromStackSlot(*MBB, It, Reg, FI, RC, &TRI, Register());434return;435}436437// To insert reload at the end of MBB, insert it before last instruction438// and then swap them.439assert(!MBB->empty() && "Empty block");440--It;441TII.loadRegFromStackSlot(*MBB, It, Reg, FI, RC, &TRI, Register());442MachineInstr *Reload = It->getPrevNode();443int Dummy = 0;444(void)Dummy;445assert(TII.isLoadFromStackSlot(*Reload, Dummy) == Reg);446assert(Dummy == FI);447MBB->remove(Reload);448MBB->insertAfter(It, Reload);449}450451// Insert reloads of (relocated) registers spilled in statepoint.452void insertReloads(MachineInstr *NewStatepoint, RegReloadCache &RC) {453MachineBasicBlock *MBB = NewStatepoint->getParent();454auto InsertPoint = std::next(NewStatepoint->getIterator());455456for (auto Reg : RegsToReload) {457insertReloadBefore(Reg, InsertPoint, MBB);458LLVM_DEBUG(dbgs() << "Reloading " << printReg(Reg, &TRI) << " from FI "459<< RegToSlotIdx[Reg] << " after statepoint\n");460461if (EHPad && !RC.hasReload(Reg, RegToSlotIdx[Reg], EHPad)) {462RC.recordReload(Reg, RegToSlotIdx[Reg], EHPad);463auto EHPadInsertPoint =464EHPad->SkipPHIsLabelsAndDebug(EHPad->begin(), Reg);465insertReloadBefore(Reg, EHPadInsertPoint, EHPad);466LLVM_DEBUG(dbgs() << "...also reload at EHPad "467<< printMBBReference(*EHPad) << "\n");468}469}470}471472// Re-write statepoint machine instruction to replace caller saved operands473// with indirect memory location (frame index).474MachineInstr *rewriteStatepoint() {475MachineInstr *NewMI =476MF.CreateMachineInstr(TII.get(MI.getOpcode()), MI.getDebugLoc(), true);477MachineInstrBuilder MIB(MF, NewMI);478479unsigned NumOps = MI.getNumOperands();480481// New indices for the remaining defs.482SmallVector<unsigned, 8> NewIndices;483unsigned NumDefs = MI.getNumDefs();484for (unsigned I = 0; I < NumDefs; ++I) {485MachineOperand &DefMO = MI.getOperand(I);486assert(DefMO.isReg() && DefMO.isDef() && "Expected Reg Def operand");487Register Reg = DefMO.getReg();488assert(DefMO.isTied() && "Def is expected to be tied");489// We skipped undef uses and did not spill them, so we should not490// proceed with defs here.491if (MI.getOperand(MI.findTiedOperandIdx(I)).isUndef()) {492if (AllowGCPtrInCSR) {493NewIndices.push_back(NewMI->getNumOperands());494MIB.addReg(Reg, RegState::Define);495}496continue;497}498if (!AllowGCPtrInCSR) {499assert(is_contained(RegsToSpill, Reg));500RegsToReload.push_back(Reg);501} else {502if (isCalleeSaved(Reg)) {503NewIndices.push_back(NewMI->getNumOperands());504MIB.addReg(Reg, RegState::Define);505} else {506NewIndices.push_back(NumOps);507RegsToReload.push_back(Reg);508}509}510}511512// Add End marker.513OpsToSpill.push_back(MI.getNumOperands());514unsigned CurOpIdx = 0;515516for (unsigned I = NumDefs; I < MI.getNumOperands(); ++I) {517MachineOperand &MO = MI.getOperand(I);518if (I == OpsToSpill[CurOpIdx]) {519int FI = RegToSlotIdx[MO.getReg()];520MIB.addImm(StackMaps::IndirectMemRefOp);521MIB.addImm(getRegisterSize(TRI, MO.getReg()));522assert(MO.isReg() && "Should be register");523assert(MO.getReg().isPhysical() && "Should be physical register");524MIB.addFrameIndex(FI);525MIB.addImm(0);526++CurOpIdx;527} else {528MIB.add(MO);529unsigned OldDef;530if (AllowGCPtrInCSR && MI.isRegTiedToDefOperand(I, &OldDef)) {531assert(OldDef < NumDefs);532assert(NewIndices[OldDef] < NumOps);533MIB->tieOperands(NewIndices[OldDef], MIB->getNumOperands() - 1);534}535}536}537assert(CurOpIdx == (OpsToSpill.size() - 1) && "Not all operands processed");538// Add mem operands.539NewMI->setMemRefs(MF, MI.memoperands());540for (auto It : RegToSlotIdx) {541Register R = It.first;542int FrameIndex = It.second;543auto PtrInfo = MachinePointerInfo::getFixedStack(MF, FrameIndex);544MachineMemOperand::Flags Flags = MachineMemOperand::MOLoad;545if (is_contained(RegsToReload, R))546Flags |= MachineMemOperand::MOStore;547auto *MMO =548MF.getMachineMemOperand(PtrInfo, Flags, getRegisterSize(TRI, R),549MFI.getObjectAlign(FrameIndex));550NewMI->addMemOperand(MF, MMO);551}552553// Insert new statepoint and erase old one.554MI.getParent()->insert(MI, NewMI);555556LLVM_DEBUG(dbgs() << "rewritten statepoint to : " << *NewMI << "\n");557MI.eraseFromParent();558return NewMI;559}560};561562class StatepointProcessor {563private:564MachineFunction &MF;565const TargetRegisterInfo &TRI;566FrameIndexesCache CacheFI;567RegReloadCache ReloadCache;568569public:570StatepointProcessor(MachineFunction &MF)571: MF(MF), TRI(*MF.getSubtarget().getRegisterInfo()),572CacheFI(MF.getFrameInfo(), TRI) {}573574bool process(MachineInstr &MI, bool AllowGCPtrInCSR) {575StatepointOpers SO(&MI);576uint64_t Flags = SO.getFlags();577// Do nothing for LiveIn, it supports all registers.578if (Flags & (uint64_t)StatepointFlags::DeoptLiveIn)579return false;580LLVM_DEBUG(dbgs() << "\nMBB " << MI.getParent()->getNumber() << " "581<< MI.getParent()->getName() << " : process statepoint "582<< MI);583CallingConv::ID CC = SO.getCallingConv();584const uint32_t *Mask = TRI.getCallPreservedMask(MF, CC);585StatepointState SS(MI, Mask, CacheFI, AllowGCPtrInCSR);586CacheFI.reset(SS.getEHPad());587588if (!SS.findRegistersToSpill())589return false;590591SS.spillRegisters();592auto *NewStatepoint = SS.rewriteStatepoint();593SS.insertReloads(NewStatepoint, ReloadCache);594return true;595}596};597} // namespace598599bool FixupStatepointCallerSaved::runOnMachineFunction(MachineFunction &MF) {600if (skipFunction(MF.getFunction()))601return false;602603const Function &F = MF.getFunction();604if (!F.hasGC())605return false;606607SmallVector<MachineInstr *, 16> Statepoints;608for (MachineBasicBlock &BB : MF)609for (MachineInstr &I : BB)610if (I.getOpcode() == TargetOpcode::STATEPOINT)611Statepoints.push_back(&I);612613if (Statepoints.empty())614return false;615616bool Changed = false;617StatepointProcessor SPP(MF);618unsigned NumStatepoints = 0;619bool AllowGCPtrInCSR = PassGCPtrInCSR;620for (MachineInstr *I : Statepoints) {621++NumStatepoints;622if (MaxStatepointsWithRegs.getNumOccurrences() &&623NumStatepoints >= MaxStatepointsWithRegs)624AllowGCPtrInCSR = false;625Changed |= SPP.process(*I, AllowGCPtrInCSR);626}627return Changed;628}629630631