Skip to content

Instantly share code, notes, and snippets.

@rofirrim

rofirrim/RISCVSoftenSpills.cpp Secret

Created Nov 22, 2020
Embed
What would you like to do?
Pass to soften spills onto FPR registers
//===----- RISCVSoftenSpills.cpp - Soften Spills using FPR registers ------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "RISCV.h"
#include "RISCVTargetMachine.h"
#include "llvm/Support/Debug.h"
#include "llvm/ADT/DenseMap.h"
using namespace llvm;
#define DEBUG_TYPE "riscv-soften-spills"
static cl::opt<bool> EnableSoftenSpills(
"riscv-soften-spills",
cl::desc("Enable softening spills using FPR registers when available"),
cl::init(false), cl::Hidden);
bool llvm::RISCVSoftenXSpillsReload(MachineFunction *MF) {
if (!EnableSoftenSpills)
return false;
// Gather some information that we will need.
MachineFrameInfo *MFI = &MF->getFrameInfo();
const MachineRegisterInfo *MRI = &MF->getRegInfo();
const RISCVSubtarget *Subtarget = &MF->getSubtarget<RISCVSubtarget>();
const RISCVInstrInfo *TII =
static_cast<const RISCVInstrInfo *>(Subtarget->getInstrInfo());
const TargetRegisterInfo *TRI = Subtarget->getRegisterInfo();
// If we have calls, for now do nothing.
// There are still opportunities here if the pair spill/reload doesn't cross
// functions but they will require a more sophisticated model.
if (MFI->hasCalls())
return false;
// If we are RV64 but we don't have D, give up.
if (Subtarget->is64Bit() && !Subtarget->hasStdExtD())
return false;
// If we are RV32 but we don't have F, give up.
if (!Subtarget->hasStdExtF())
return false;
bool Changed = false;
// Flow-insensitive analysis in which we identify FPR32/FPR64 that
// are not used at all.
// There are further opportunities as the static path for a spill and all its
// reloads might have free FPR registers. However our model is very simple so
// we can't represent these.
LiveRegUnits LRU(*TRI);
for (auto &MBB : *MF) {
for (auto &MI : MBB) {
LRU.accumulate(MI);
}
}
const MCPhysReg *CalleeSavedRegs = MRI->getCalleeSavedRegs();
auto IsCalleeSaved = [&](MCPhysReg Reg) {
for (const MCPhysReg *R = CalleeSavedRegs; *R; R++)
if (*R == Reg)
return true;
return false;
};
BitVector RegsAvailableFPR(TRI->getNumRegs());
const TargetRegisterClass &FPRRegClass =
Subtarget->is64Bit() ? RISCV::FPR64RegClass : RISCV::FPR32RegClass;
for (MCPhysReg PhysReg : FPRRegClass.getRegisters()) {
if (LRU.available(PhysReg) && !IsCalleeSaved(PhysReg))
RegsAvailableFPR.set(PhysReg);
}
// Give up if we don't have any available FPRs.
if (RegsAvailableFPR.empty())
return false;
// Helper used to identify spills and reloads.
auto IsSpillReload = [&](MachineInstr &MI) {
int FI = 0;
bool Result = false;
bool IsSpill = false;
switch (MI.getOpcode()) {
default:
break;
case RISCV::LD:
case RISCV::LW:
if (TII->isLoadFromStackSlot(MI, FI))
Result = true;
break;
case RISCV::SD:
case RISCV::SW:
if (TII->isStoreToStackSlot(MI, FI)) {
Result = true;
IsSpill = true;
}
break;
}
return std::make_tuple(Result, FI, IsSpill);
};
using FrameIndexToFPRTy = DenseMap<unsigned, Register>;
FrameIndexToFPRTy FrameIndexToFPR;
// Check frame indexes in other instructions and assign them to NoRegister
// to avoid replacing them.
for (auto &MBB : *MF) {
for (MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
MBBI != E; MBBI++) {
MachineInstr &MI = *MBBI;
bool SpillOrReload;
int FI;
bool IsSpill;
std::tie(SpillOrReload, FI, IsSpill) = IsSpillReload(MI);
if (SpillOrReload)
continue;
for (MachineOperand &MO : MI.operands()) {
if (MO.isFI())
FrameIndexToFPR[MO.getIndex()] = RISCV::NoRegister;
}
}
}
// Now replace the spills and reloads.
for (auto &MBB : *MF) {
MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
while (MBBI != E) {
MachineBasicBlock::iterator NMBBI = std::next(MBBI);
MachineInstr &MI = *MBBI;
MBBI = NMBBI;
bool SpillOrReload;
int FI;
bool IsSpill;
std::tie(SpillOrReload, FI, IsSpill) = IsSpillReload(MI);
if (!SpillOrReload)
continue;
LLVM_DEBUG(llvm::dbgs() << "Found ");
LLVM_DEBUG(MI.print(llvm::dbgs()));
FrameIndexToFPRTy::iterator ItR;
bool FINotFound;
std::tie(ItR, FINotFound) =
FrameIndexToFPR.insert(std::make_pair(FI, RISCV::NoRegister));
if (FINotFound) {
// Try to find a suitable free FPR.
LLVM_DEBUG(llvm::dbgs()
<< "Trying to find a free FPR for index " << FI << "\n");
Register R = RISCV::NoRegister;
int Idx = RegsAvailableFPR.find_first();
if (Idx > 0) {
RegsAvailableFPR.reset(Idx);
R = Idx;
}
ItR->second = R;
if (R != RISCV::NoRegister) {
MFI->RemoveStackObject(FI);
}
}
Register R = ItR->second;
if (R == RISCV::NoRegister) {
LLVM_DEBUG(llvm::dbgs()
<< "No register is available for index " << FI << "\n\n");
continue;
}
LLVM_DEBUG(llvm::dbgs()
<< "We have a register " << R << " for " << FI << "\n");
// Ok so we found a suitable FPR, let's use that one.
if (IsSpill) {
unsigned Opcode =
Subtarget->is64Bit() ? RISCV::FMV_D_X : RISCV::FMV_W_X;
MachineInstr &NewMI =
*BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(Opcode), R)
.addReg(MI.getOperand(0).getReg());
LLVM_DEBUG(llvm::dbgs() << "Replacing with ");
LLVM_DEBUG(NewMI.print(llvm::dbgs()));
LLVM_DEBUG(llvm::dbgs() << "\n");
} else {
unsigned Opcode =
Subtarget->is64Bit() ? RISCV::FMV_X_D : RISCV::FMV_X_W;
MachineInstr &NewMI =
*BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(Opcode),
MI.getOperand(0).getReg())
.addReg(R);
LLVM_DEBUG(llvm::dbgs() << "Replacing with ");
LLVM_DEBUG(NewMI.print(llvm::dbgs()));
LLVM_DEBUG(llvm::dbgs() << "\n");
}
MI.eraseFromParent();
Changed = true;
}
}
return Changed;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.