Reference reference
PEI (Prolog Epilog Inserter) pass creation:
//
void TargetPassConfig::addMachinePasses() {
//...
// Insert prolog/epilog code. Eliminate abstract frame index references...
if (getOptLevel() != CodeGenOpt::None) {
addPass(&PostRAMachineSinkingID); // sink COPY instructions which should be handled after RA.
addPass(&ShrinkWrapID); // determine where the safe point to insert the prologue and epilogue.
}
// Prolog/Epilog inserter needs a TargetMachine to instantiate. But only
// do so if it hasn't been disabled, substituted, or overridden.
if (!isPassSubstitutedOrOverridden(&PrologEpilogCodeInserterID))
addPass(createPrologEpilogInserterPass());
}
Terminologies:
Important functions:
calculateCallFrameInfo(MF)
. Caculate the MaxCallFrameSize and AdjustsStack variables for the functions fram information. Also eliminates call frame pseudo instructions.calculateSaveRestoreBlocks(MF)
. determine the placement of CSR spill/restore code and prolog/epilog code: place all spills in the entry block, all restores in return block.spillCalleeSavedRegs(MF)
. CSR spilling and restoring, for targets that need it.TFI->processFunctionBeforeFrameFinalized(MF,RS)
. This allows the target machine to make final modifications to the function before the frame layout is finalized.calculateFrameObjectOffsets(MF)
. Calculate actual frame offsets for all abstract stack objects.insertPrologEpilogCode(MF)
. Adding prolog/epilog code to the function.TFI->processFunctionBeforeFrameIndicesReplaced(MF,RS)
. This allows target machine to make final modifications to the function before the frame layout is finalized.replaceFrameIndices(MF)
. Replace all MO_FrameIndex operands with physical register references and actual offsets.Machine Function pass entry:
// llvm/lib/CodeGen/PrologEpilogInserter.cpp
/// runOnMachineFunction - Insert prolog/epilog code and replace abstract
/// frame indexes with appropriate references.
bool PEI::runOnMachineFunction(MachineFunction &MF) {
NumFuncSeen++;
const Function &F = MF.getFunction();
const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo();
const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering();
RS = TRI->requiresRegisterScavenging(MF) ? new RegScavenger() : nullptr;
FrameIndexVirtualScavenging = TRI->requiresFrameIndexScavenging(MF);
ORE = &getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE();
// Calculate the MaxCallFrameSize and AdjustsStack variables for the
// function's frame information. Also eliminates call frame pseudo
// instructions.
calculateCallFrameInfo(MF);
// Determine placement of CSR spill/restore code and prolog/epilog code:
// place all spills in the entry block, all restores in return blocks.
calculateSaveRestoreBlocks(MF);
// Stash away DBG_VALUEs that should not be moved by insertion of prolog code.
SavedDbgValuesMap EntryDbgValues;
for (MachineBasicBlock *SaveBlock : SaveBlocks)
stashEntryDbgValues(*SaveBlock, EntryDbgValues);
// Handle CSR spilling and restoring, for targets that need it.
if (MF.getTarget().usesPhysRegsForPEI())
spillCalleeSavedRegs(MF);
// Allow the target machine to make final modifications to the function
// before the frame layout is finalized.
TFI->processFunctionBeforeFrameFinalized(MF, RS);
// Calculate actual frame offsets for all abstract stack objects...
calculateFrameObjectOffsets(MF);
// Add prolog and epilog code to the function. This function is required
// to align the stack frame as necessary for any stack variables or
// called functions. Because of this, calculateCalleeSavedRegisters()
// must be called before this function in order to set the AdjustsStack
// and MaxCallFrameSize variables.
if (!F.hasFnAttribute(Attribute::Naked))
insertPrologEpilogCode(MF);
// Reinsert stashed debug values at the start of the entry blocks.
for (auto &I : EntryDbgValues)
I.first->insert(I.first->begin(), I.second.begin(), I.second.end());
// Replace all MO_FrameIndex operands with physical register references
// and actual offsets.
//
replaceFrameIndices(MF);
// If register scavenging is needed, as we've enabled doing it as a
// post-pass, scavenge the virtual registers that frame index elimination
// inserted.
if (TRI->requiresRegisterScavenging(MF) && FrameIndexVirtualScavenging)
scavengeFrameVirtualRegs(MF, *RS);
// Warn on stack size when we exceeds the given limit.
MachineFrameInfo &MFI = MF.getFrameInfo();
uint64_t StackSize = MFI.getStackSize();
if (WarnStackSize.getNumOccurrences() > 0 && WarnStackSize < StackSize) {
DiagnosticInfoStackSize DiagStackSize(F, StackSize);
F.getContext().diagnose(DiagStackSize);
}
ORE->emit([&]() {
return MachineOptimizationRemarkAnalysis(DEBUG_TYPE, "StackSize",
MF.getFunction().getSubprogram(),
&MF.front())
<< ore::NV("NumStackBytes", StackSize) << " stack bytes in function";
});
delete RS;
SaveBlocks.clear();
RestoreBlocks.clear();
MFI.setSavePoint(nullptr);
MFI.setRestorePoint(nullptr);
return true;
}
TODO.
Compute the lists of blocks.
// llvm/lib/CodeGen/PrologEpilogInserter.cpp
// void PEI::calculateSaveRestoreBlocks(MachineFunction &MF) {..
for (MachineBasicBlock &MBB : MF) {
if (MBB.isEHFuncletEntry())
SaveBlocks.push_back(&MBB);
if (MBB.isReturnBlock())
RestoreBlocks.push_back(&MBB);
}
for (MachineBasicBlock *SaveBlock : SaveBlocks) {
insertCSRSaves(*SaveBlock, CSI);
// Update the live-in information of all the blocks up to the save
// point.
updateLiveness(MF);
}
for (MachineBasicBlock *RestoreBlock : RestoreBlocks)
insertCSRRestores(*RestoreBlock, CSI);
insertCSRSaves
will call target dependent spill function TFI->spillCalleeSavedRegisters()
. See Mips for example.
More code tracking:
// llvm/lib/CodeGen/PrologEpilogInserter.cpp
void PEI::spillCalleeSavedRegs(MachineFunction &MF) {
// ...
// Determine which of the registers in the callee save list should be saved.
BitVector SavedRegs;
TFI->determineCalleeSaves(MF, SavedRegs, RS);
// Assign stack slots for any callee-saved registers that must be spilled.
assignCalleeSavedSpillSlots(MF, SavedRegs, MinCSFrameIndex, MaxCSFrameIndex);
// Add the code to save and restore the callee saved registers.
if (!F.hasFnAttribute(Attribute::Naked)) {
MFI.setCalleeSavedInfoValid(true);
std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
if (!CSI.empty()) {
if (!MFI.hasCalls())
NumLeafFuncWithSpills++;
for (MachineBasicBlock *SaveBlock : SaveBlocks) {
insertCSRSaves(*SaveBlock, CSI);
// Update the live-in information of all the blocks up to the save
// point.
updateLiveness(MF);
}
for (MachineBasicBlock *RestoreBlock : RestoreBlocks)
insertCSRRestores(*RestoreBlock, CSI);
}
}
}
/// insertCSRSaves
/// Insert restore code for the callee-saved registers used in the function.
static void insertCSRSaves(MachineBasicBlock &SaveBlock,
ArrayRef<CalleeSavedInfo> CSI) {
MachineFunction &MF = *SaveBlock.getParent();
const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo();
const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering();
const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo();
MachineBasicBlock::iterator I = SaveBlock.begin();
if (!TFI->spillCalleeSavedRegisters(SaveBlock, I, CSI, TRI)) {
// only reach here if target impl is not available
for (const CalleeSavedInfo &CS : CSI) {
// Insert the spill to the stack frame.
unsigned Reg = CS.getReg();
if (CS.isSpilledToReg()) {
BuildMI(SaveBlock, I, DebugLoc(),
TII.get(TargetOpcode::COPY), CS.getDstReg())
.addReg(Reg, getKillRegState(true));
} else {
const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg);
TII.storeRegToStackSlot(SaveBlock, I, Reg, true, CS.getFrameIdx(), RC,
TRI);
}
}
}
}
/// insertCSRRestores
/// Insert restore code for the callee-saved registers used in the function.
static void insertCSRRestores(MachineBasicBlock &RestoreBlock,
std::vector<CalleeSavedInfo> &CSI) {
MachineFunction &MF = *RestoreBlock.getParent();
const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo();
const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering();
const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo();
// Restore all registers immediately before the return and any
// terminators that precede it.
MachineBasicBlock::iterator I = RestoreBlock.getFirstTerminator();
if (!TFI->restoreCalleeSavedRegisters(RestoreBlock, I, CSI, TRI)) {
for (const CalleeSavedInfo &CI : reverse(CSI)) {
unsigned Reg = CI.getReg();
if (CI.isSpilledToReg()) {
BuildMI(RestoreBlock, I, DebugLoc(), TII.get(TargetOpcode::COPY), Reg)
.addReg(CI.getDstReg(), getKillRegState(true));
} else {
const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg);
TII.loadRegFromStackSlot(RestoreBlock, I, Reg, CI.getFrameIdx(), RC, TRI);
assert(I != RestoreBlock.begin() &&
"loadRegFromStackSlot didn't insert any code!");
// Insert in reverse order. loadRegFromStackSlot can insert
// multiple instructions.
}
}
}
}
Calculate actual frame offsets for all of the abstract stack objects.
Code snippet:
// llvm/lib/CodeGen/PrologEpilogInserter.cpp
// PEI::calculateFrameObjectOffsets
...
}else if (MaxCSFrameIndex >= MinCSFrameIndex) {
// Be careful about underflow in comparisons agains MinCSFrameIndex.
for (unsigned i = MaxCSFrameIndex; i != MinCSFrameIndex - 1; --i) {
if (MFI.getStackID(i) !=
TargetStackID::Default) // Only allocate objects on the default stack.
continue;
if (MFI.isDeadObjectIndex(i))
continue;
// Adjust to alignment boundary
Offset = alignTo(Offset, MFI.getObjectAlign(i), Skew);
LLVM_DEBUG(dbgs() << "alloc FI(" << i << ") at SP[" << Offset << "]\n");
MFI.setObjectOffset(i, Offset);
Offset += MFI.getObjectSize(i);
}
}
...
Call
TFI.emitPrologue(MF, *SaveBlock)
TFI.emitEpilogue(MF, *RestoreBlock)
TFI.inlineStackProbe(MF, *SaveBlock)
TFI.adjustForHiPEPrologue(MF, *SaveBlock)
See MIPS for target dependent TargetFrameLowering implementation.
// llvm/lib/CodeGen/PrologEpilogInserter.cpp
/// insertPrologEpilogCode - Scan the function for modified callee saved
/// registers, insert spill code for these callee saved registers, then add
/// prolog and epilog code to the function.
void PEI::insertPrologEpilogCode(MachineFunction &MF) {
const TargetFrameLowering &TFI = *MF.getSubtarget().getFrameLowering();
// Add prologue to the function...
for (MachineBasicBlock *SaveBlock : SaveBlocks)
TFI.emitPrologue(MF, *SaveBlock);
// Add epilogue to restore the callee-save registers in each exiting block.
for (MachineBasicBlock *RestoreBlock : RestoreBlocks)
TFI.emitEpilogue(MF, *RestoreBlock);
for (MachineBasicBlock *SaveBlock : SaveBlocks)
TFI.inlineStackProbe(MF, *SaveBlock);
// Emit additional code that is required to support segmented stacks, if
// we've been asked for it. This, when linked with a runtime with support
// for segmented stacks (libgcc is one), will result in allocating stack
// space in small chunks instead of one large contiguous block.
if (MF.shouldSplitStack()) {
for (MachineBasicBlock *SaveBlock : SaveBlocks)
TFI.adjustForSegmentedStacks(MF, *SaveBlock);
// Record that there are split-stack functions, so we will emit a
// special section to tell the linker.
MF.getMMI().setHasSplitStack(true);
} else
MF.getMMI().setHasNosplitStack(true);
// Emit additional code that is required to explicitly handle the stack in
// HiPE native code (if needed) when loaded in the Erlang/OTP runtime. The
// approach is rather similar to that of Segmented Stacks, but it uses a
// different conditional check and another BIF for allocating more stack
// space.
if (MF.getFunction().getCallingConv() == CallingConv::HiPE)
for (MachineBasicBlock *SaveBlock : SaveBlocks)
TFI.adjustForHiPEPrologue(MF, *SaveBlock);
}
References: reference Target dependent implementation of prologue/epilogue emission. See [../] for callers. MipsSEFrameLowering::emitPrologue Adjust stack pointer: MipsSEFrameLowering::emitPrologue() => TII.adjustStackPtr(SP, -StackSize, MBB, MBBI) MipsSEInstrInfo::adjustStackPtr() => BuildMI // addi sp, sp, amount MipsSEFrameLowering::spillCalleeSavedRegisters The instruction to spill return address $ra register is built here. // search Mips::RA_64, spill RA as callee saved reg MipsSEFrameLowering::spillCalleeSavedRegisters(){ //... DebugLL("before spill callee save: block: "; MBB.dump();); for (unsigned i = 0, e = CSI.
If you could revise
the fundmental principles of
computer system design
to improve security...
... what would you change?