Skip to content

Commit

Permalink
[WinEH] Emit prologues and epilogues for funclets
Browse files Browse the repository at this point in the history
Summary:
32-bit funclets have short prologues that allocate enough stack for the
largest call in the whole function. The runtime saves CSRs for the
funclet. It doesn't restore CSRs after we finally transfer control back
to the parent funciton via a CATCHRET, but that's a separate issue.
32-bit funclets also have to adjust the incoming EBP value, which is
what llvm.x86.seh.recoverframe does in the old model.

64-bit funclets need to spill CSRs as normal. For simplicity, this just
spills the same set of CSRs as the parent function, rather than trying
to compute different CSR sets for the parent function and each funclet.
64-bit funclets also allocate enough stack space for the largest
outgoing call frame, like 32-bit.

Reviewers: majnemer

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D12546

llvm-svn: 247092
  • Loading branch information
rnk committed Sep 8, 2015
1 parent 18203f7 commit df12951
Show file tree
Hide file tree
Showing 11 changed files with 399 additions and 57 deletions.
3 changes: 3 additions & 0 deletions llvm/include/llvm/CodeGen/WinEHFuncInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "llvm/ADT/DenseMap.h"

namespace llvm {
class AllocaInst;
class BasicBlock;
class Constant;
class Function;
Expand Down Expand Up @@ -156,6 +157,8 @@ struct WinEHFuncInfo {
/// localescape index of the 32-bit EH registration node. Set by
/// WinEHStatePass and used indirectly by SEH filter functions of the parent.
int EHRegNodeEscapeIndex = INT_MAX;
const AllocaInst *EHRegNode = nullptr;
int EHRegNodeFrameIndex = INT_MAX;

WinEHFuncInfo() {}
};
Expand Down
20 changes: 14 additions & 6 deletions llvm/lib/CodeGen/AsmPrinter/WinException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,12 +473,20 @@ void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) {
OS.EmitValue(create32bitRef(HT.Handler), 4);

if (shouldEmitPersonality) {
MCSymbol *ParentFrameOffset =
Asm->OutContext.getOrCreateParentFrameOffsetSymbol(
GlobalValue::getRealLinkageName(HT.Handler->getName()));
const MCSymbolRefExpr *ParentFrameOffsetRef = MCSymbolRefExpr::create(
ParentFrameOffset, Asm->OutContext);
OS.EmitValue(ParentFrameOffsetRef, 4); // ParentFrameOffset
if (FuncInfo.CatchHandlerParentFrameObjOffset.empty()) {
// With the new IR, this is always 16 + 8 + getMaxCallFrameSize().
// Keep this in sync with X86FrameLowering::emitPrologue.
int ParentFrameOffset =
16 + 8 + MF->getFrameInfo()->getMaxCallFrameSize();
OS.EmitIntValue(ParentFrameOffset, 4); // ParentFrameOffset
} else {
MCSymbol *ParentFrameOffset =
Asm->OutContext.getOrCreateParentFrameOffsetSymbol(
GlobalValue::getRealLinkageName(HT.Handler->getName()));
const MCSymbolRefExpr *ParentFrameOffsetRef =
MCSymbolRefExpr::create(ParentFrameOffset, Asm->OutContext);
OS.EmitValue(ParentFrameOffsetRef, 4); // ParentFrameOffset
}
}
}
}
Expand Down
56 changes: 32 additions & 24 deletions llvm/lib/CodeGen/PrologEpilogInserter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ class PEI : public MachineFunctionPass {
// stack frame indexes.
unsigned MinCSFrameIndex, MaxCSFrameIndex;

// Save and Restore blocks of the current function.
MachineBasicBlock *SaveBlock;
// Save and Restore blocks of the current function. Typically there is a
// single save block, unless Windows EH funclets are involved.
SmallVector<MachineBasicBlock *, 1> SaveBlocks;
SmallVector<MachineBasicBlock *, 4> RestoreBlocks;

// Flag to control whether to use the register scavenger to resolve
Expand Down Expand Up @@ -142,7 +143,7 @@ void PEI::calculateSets(MachineFunction &Fn) {

// Use the points found by shrink-wrapping, if any.
if (MFI->getSavePoint()) {
SaveBlock = MFI->getSavePoint();
SaveBlocks.push_back(MFI->getSavePoint());
assert(MFI->getRestorePoint() && "Both restore and save must be set");
MachineBasicBlock *RestoreBlock = MFI->getRestorePoint();
// If RestoreBlock does not have any successor and is not a return block
Expand All @@ -154,13 +155,13 @@ void PEI::calculateSets(MachineFunction &Fn) {
}

// Save refs to entry and return blocks.
SaveBlock = Fn.begin();
for (MachineFunction::iterator MBB = Fn.begin(), E = Fn.end();
MBB != E; ++MBB)
if (isReturnBlock(MBB))
RestoreBlocks.push_back(MBB);

return;
SaveBlocks.push_back(Fn.begin());
for (MachineBasicBlock &MBB : Fn) {
if (MBB.isEHFuncletEntry())
SaveBlocks.push_back(&MBB);
if (isReturnBlock(&MBB))
RestoreBlocks.push_back(&MBB);
}
}

/// StackObjSet - A set of stack object indexes
Expand Down Expand Up @@ -237,6 +238,7 @@ bool PEI::runOnMachineFunction(MachineFunction &Fn) {
}

delete RS;
SaveBlocks.clear();
RestoreBlocks.clear();
return true;
}
Expand Down Expand Up @@ -446,18 +448,20 @@ void PEI::insertCSRSpillsAndRestores(MachineFunction &Fn) {
MachineBasicBlock::iterator I;

// Spill using target interface.
I = SaveBlock->begin();
if (!TFI->spillCalleeSavedRegisters(*SaveBlock, I, CSI, TRI)) {
for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
// Insert the spill to the stack frame.
unsigned Reg = CSI[i].getReg();
const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg);
TII.storeRegToStackSlot(*SaveBlock, I, Reg, true, CSI[i].getFrameIdx(),
RC, TRI);
for (MachineBasicBlock *SaveBlock : SaveBlocks) {
I = SaveBlock->begin();
if (!TFI->spillCalleeSavedRegisters(*SaveBlock, I, CSI, TRI)) {
for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
// Insert the spill to the stack frame.
unsigned Reg = CSI[i].getReg();
const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg);
TII.storeRegToStackSlot(*SaveBlock, I, Reg, true, CSI[i].getFrameIdx(),
RC, TRI);
}
}
// Update the live-in information of all the blocks up to the save point.
updateLiveness(Fn);
}
// Update the live-in information of all the blocks up to the save point.
updateLiveness(Fn);

// Restore using target interface.
for (MachineBasicBlock *MBB : RestoreBlocks) {
Expand Down Expand Up @@ -771,7 +775,8 @@ void PEI::insertPrologEpilogCode(MachineFunction &Fn) {
const TargetFrameLowering &TFI = *Fn.getSubtarget().getFrameLowering();

// Add prologue to the function...
TFI.emitPrologue(Fn, *SaveBlock);
for (MachineBasicBlock *SaveBlock : SaveBlocks)
TFI.emitPrologue(Fn, *SaveBlock);

// Add epilogue to restore the callee-save registers in each exiting block.
for (MachineBasicBlock *RestoreBlock : RestoreBlocks)
Expand All @@ -781,16 +786,19 @@ void PEI::insertPrologEpilogCode(MachineFunction &Fn) {
// 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 (Fn.shouldSplitStack())
TFI.adjustForSegmentedStacks(Fn, *SaveBlock);
if (Fn.shouldSplitStack()) {
for (MachineBasicBlock *SaveBlock : SaveBlocks)
TFI.adjustForSegmentedStacks(Fn, *SaveBlock);
}

// 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 (Fn.getFunction()->getCallingConv() == CallingConv::HiPE)
TFI.adjustForHiPEPrologue(Fn, *SaveBlock);
for (MachineBasicBlock *SaveBlock : SaveBlocks)
TFI.adjustForHiPEPrologue(Fn, *SaveBlock);
}

/// replaceFrameIndices - Replace all MO_FrameIndex operands with physical
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,12 @@ void FunctionLoweringInfo::set(const Function &fn, MachineFunction &mf,
for (WinEHHandlerType &H : TBME.HandlerArray)
if (const auto *BB = dyn_cast<BasicBlock>(H.Handler))
H.HandlerMBB = MBBMap[BB];
// If there's an explicit EH registration node on the stack, record its
// frame index.
if (EHInfo.EHRegNode && EHInfo.EHRegNode->getParent()->getParent() == Fn) {
assert(StaticAllocaMap.count(EHInfo.EHRegNode));
EHInfo.EHRegNodeFrameIndex = StaticAllocaMap[EHInfo.EHRegNode];
}
}

// Copy the state numbers to LandingPadInfo for the current function, which
Expand Down
144 changes: 136 additions & 8 deletions llvm/lib/Target/X86/X86FrameLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/WinEHFuncInfo.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Function.h"
#include "llvm/MC/MCAsmInfo.h"
Expand Down Expand Up @@ -88,7 +89,7 @@ bool X86FrameLowering::hasFP(const MachineFunction &MF) const {
MFI->hasVarSizedObjects() ||
MFI->isFrameAddressTaken() || MFI->hasOpaqueSPAdjustment() ||
MF.getInfo<X86MachineFunctionInfo>()->getForceFramePointer() ||
MMI.callsUnwindInit() || MMI.callsEHReturn() ||
MMI.callsUnwindInit() || MMI.hasEHFunclets() || MMI.callsEHReturn() ||
MFI->hasStackMap() || MFI->hasPatchPoint());
}

Expand Down Expand Up @@ -695,7 +696,40 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
uint64_t NumBytes = 0;
int stackGrowth = -SlotSize;

if (HasFP) {
if (MBB.isEHFuncletEntry()) {
assert(STI.isOSWindows() && "funclets only supported on Windows");

// Set up the FramePtr and BasePtr physical registers using the address
// passed as EBP or RDX by the MSVC EH runtime.
if (STI.is32Bit()) {
MBBI = restoreWin32EHFrameAndBasePtr(MBB, MBBI, DL);
} else {
// FIXME: Add SEH directives.
NeedsWinCFI = false;
// Immediately spill RDX into the home slot. The runtime cares about this.
unsigned RDX = Uses64BitFramePtr ? X86::RDX : X86::EDX;
// MOV64mr %rdx, 16(%rsp)
unsigned MOVmr = Uses64BitFramePtr ? X86::MOV64mr : X86::MOV32mr;
addRegOffset(BuildMI(MBB, MBBI, DL, TII.get(MOVmr)),
StackPtr, true, 16)
.addReg(RDX)
.setMIFlag(MachineInstr::FrameSetup);
// PUSH64r %rbp
BuildMI(MBB, MBBI, DL, TII.get(Is64Bit ? X86::PUSH64r : X86::PUSH32r))
.addReg(MachineFramePtr, RegState::Kill)
.setMIFlag(MachineInstr::FrameSetup);
// MOV64rr %rdx, %rbp
unsigned MOVrr = Uses64BitFramePtr ? X86::MOV64rr : X86::MOV32rr;
BuildMI(MBB, MBBI, DL, TII.get(MOVrr), FramePtr)
.addReg(RDX)
.setMIFlag(MachineInstr::FrameSetup);
assert(!TRI->hasBasePointer(MF) &&
"x64 funclets with base ptrs not yet implemented");
}

// For EH funclets, only allocate enough space for outgoing calls.
NumBytes = MFI->getMaxCallFrameSize();
} else if (HasFP) {
// Calculate required stack adjustment.
uint64_t FrameSize = StackSize - SlotSize;
// If required, include space for extra hidden slot for stashing base pointer.
Expand Down Expand Up @@ -956,10 +990,11 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
// it recovers the frame pointer from the base pointer rather than the
// other way around.
unsigned Opm = Uses64BitFramePtr ? X86::MOV64mr : X86::MOV32mr;
unsigned IgnoredFrameReg;
addRegOffset(BuildMI(MBB, MBBI, DL, TII.get(Opm)), BasePtr, true,
getFrameIndexReference(MF, X86FI->getSEHFramePtrSaveIndex(),
IgnoredFrameReg))
unsigned UsedReg;
int Offset =
getFrameIndexReference(MF, X86FI->getSEHFramePtrSaveIndex(), UsedReg);
assert(UsedReg == BasePtr);
addRegOffset(BuildMI(MBB, MBBI, DL, TII.get(Opm)), UsedReg, true, Offset)
.addReg(FramePtr)
.setMIFlag(MachineInstr::FrameSetup);
}
Expand Down Expand Up @@ -991,6 +1026,17 @@ bool X86FrameLowering::canUseLEAForSPInEpilogue(
return !MF.getTarget().getMCAsmInfo()->usesWindowsCFI() || hasFP(MF);
}

static bool isFuncletReturnInstr(MachineInstr *MI) {
switch (MI->getOpcode()) {
case X86::CATCHRET:
case X86::CATCHRET64:
return true;
default:
return false;
}
llvm_unreachable("impossible");
}

void X86FrameLowering::emitEpilogue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
const MachineFrameInfo *MFI = MF.getFrameInfo();
Expand All @@ -1016,7 +1062,16 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF,
unsigned CSSize = X86FI->getCalleeSavedFrameSize();
uint64_t NumBytes = 0;

if (hasFP(MF)) {
if (isFuncletReturnInstr(MBBI)) {
NumBytes = MFI->getMaxCallFrameSize();

if (Is64Bit) {
assert(hasFP(MF) && "win64 EH funclets without FP not yet implemented");
// POP64r %rbp
BuildMI(MBB, MBBI, DL, TII.get(Is64Bit ? X86::POP64r : X86::POP32r),
MachineFramePtr);
}
} else if (hasFP(MF)) {
// Calculate required stack adjustment.
uint64_t FrameSize = StackSize - SlotSize;
NumBytes = FrameSize - CSSize;
Expand Down Expand Up @@ -1331,6 +1386,11 @@ bool X86FrameLowering::spillCalleeSavedRegisters(
const TargetRegisterInfo *TRI) const {
DebugLoc DL = MBB.findDebugLoc(MI);

// Don't save CSRs in 32-bit EH funclets. The caller saves EBX, EBP, ESI, EDI
// for us, and there are no XMM CSRs on Win32.
if (MBB.isEHFuncletEntry() && STI.is32Bit() && STI.isOSWindows())
return true;

// Push GPRs. It increases frame size.
unsigned Opc = STI.is64Bit() ? X86::PUSH64r : X86::PUSH32r;
for (unsigned i = CSI.size(); i != 0; --i) {
Expand Down Expand Up @@ -1372,6 +1432,11 @@ bool X86FrameLowering::restoreCalleeSavedRegisters(MachineBasicBlock &MBB,
if (CSI.empty())
return false;

// Don't restore CSRs in 32-bit EH funclets. Matches
// spillCalleeSavedRegisters.
if (isFuncletReturnInstr(MI) && STI.is32Bit() && STI.isOSWindows())
return true;

DebugLoc DL = MBB.findDebugLoc(MI);

// Reload XMMs from stack frame.
Expand Down Expand Up @@ -1423,8 +1488,16 @@ void X86FrameLowering::determineCalleeSaves(MachineFunction &MF,
}

// Spill the BasePtr if it's used.
if (TRI->hasBasePointer(MF))
if (TRI->hasBasePointer(MF)) {
SavedRegs.set(TRI->getBaseRegister());

// Allocate a spill slot for EBP if we have a base pointer and EH funclets.
if (MF.getMMI().hasEHFunclets()) {
int FI = MFI->CreateSpillStackObject(SlotSize, SlotSize);
X86FI->setHasSEHFramePtrSave(true);
X86FI->setSEHFramePtrSaveIndex(FI);
}
}
}

static bool
Expand Down Expand Up @@ -1976,3 +2049,58 @@ bool X86FrameLowering::canUseAsEpilogue(const MachineBasicBlock &MBB) const {
// safe to insert the epilogue here.
return !terminatorsNeedFlagsAsInput(MBB);
}

MachineBasicBlock::iterator X86FrameLowering::restoreWin32EHFrameAndBasePtr(
MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
DebugLoc DL) const {
assert(STI.isTargetWindowsMSVC() && "funclets only supported in MSVC env");
assert(STI.isTargetWin32() && "EBP/ESI restoration only required on win32");
assert(STI.is32Bit() && !Uses64BitFramePtr &&
"restoring EBP/ESI on non-32-bit target");

MachineFunction &MF = *MBB.getParent();
unsigned FramePtr = TRI->getFrameRegister(MF);
unsigned BasePtr = TRI->getBaseRegister();
MachineModuleInfo &MMI = MF.getMMI();
const Function *Fn = MF.getFunction();
WinEHFuncInfo &FuncInfo = MMI.getWinEHFuncInfo(Fn);
X86MachineFunctionInfo *X86FI = MF.getInfo<X86MachineFunctionInfo>();
MachineFrameInfo *MFI = MF.getFrameInfo();

// FIXME: Don't set FrameSetup flag in catchret case.

int FI = FuncInfo.EHRegNodeFrameIndex;
unsigned UsedReg;
int EHRegOffset = getFrameIndexReference(MF, FI, UsedReg);
int EHRegSize = MFI->getObjectSize(FI);
int EndOffset = -EHRegOffset - EHRegSize;
assert(EndOffset >= 0 &&
"end of registration object above normal EBP position!");
if (UsedReg == FramePtr) {
// ADD $offset, %ebp
assert(UsedReg == FramePtr);
unsigned ADDri = getADDriOpcode(false, EndOffset);
BuildMI(MBB, MBBI, DL, TII.get(ADDri), FramePtr)
.addReg(FramePtr)
.addImm(EndOffset)
.setMIFlag(MachineInstr::FrameSetup)
->getOperand(3)
.setIsDead();
} else {
assert(UsedReg == BasePtr);
// LEA offset(%ebp), %esi
addRegOffset(BuildMI(MBB, MBBI, DL, TII.get(X86::LEA32r), BasePtr),
FramePtr, false, EndOffset)
.setMIFlag(MachineInstr::FrameSetup);
// MOV32mr SavedEBPOffset(%esi), %ebp
assert(X86FI->getHasSEHFramePtrSave());
int Offset =
getFrameIndexReference(MF, X86FI->getSEHFramePtrSaveIndex(), UsedReg);
assert(UsedReg == BasePtr);
addRegOffset(BuildMI(MBB, MBBI, DL, TII.get(X86::MOV32mr)), UsedReg, true,
Offset)
.addReg(FramePtr)
.setMIFlag(MachineInstr::FrameSetup);
}
return MBBI;
}
7 changes: 7 additions & 0 deletions llvm/lib/Target/X86/X86FrameLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ class X86FrameLowering : public TargetFrameLowering {
MachineBasicBlock::iterator MBBI,
DebugLoc DL, int64_t Offset,
bool InEpilogue) const;

/// Sets up EBP and optionally ESI based on the incoming EBP value. Only
/// needed for 32-bit. Used in funclet prologues and at catchret destinations.
MachineBasicBlock::iterator
restoreWin32EHFrameAndBasePtr(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
DebugLoc DL) const;
};

} // End llvm namespace
Expand Down
Loading

0 comments on commit df12951

Please sign in to comment.