Skip to content

Commit

Permalink
[WinEH] Make llvm.x86.seh.restoreframe work for stack realignment pro…
Browse files Browse the repository at this point in the history
…logues

The incoming EBP value points to the end of a local stack allocation, so
we can use that to restore ESI, the base pointer. Once we do that, we
can use local stack allocations. If we know we need stack realignment,
spill the original frame pointer in the prologue and reload it after
restoring ESI.

llvm-svn: 241648
  • Loading branch information
rnk committed Jul 7, 2015
1 parent 84936e0 commit e69bdb8
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 41 deletions.
14 changes: 7 additions & 7 deletions llvm/include/llvm/CodeGen/MachineFrameInfo.h
Expand Up @@ -229,9 +229,9 @@ class MachineFrameInfo {
/// Whether the "realign-stack" option is on.
bool RealignOption;

/// True if the function includes inline assembly that adjusts the stack
/// pointer.
bool HasInlineAsmWithSPAdjust;
/// True if the function dynamically adjusts the stack pointer through some
/// opaque mechanism like inline assembly or Win32 EH.
bool HasOpaqueSPAdjustment;

/// True if the function contains a call to the llvm.vastart intrinsic.
bool HasVAStart;
Expand Down Expand Up @@ -269,7 +269,7 @@ class MachineFrameInfo {
LocalFrameSize = 0;
LocalFrameMaxAlign = 0;
UseLocalStackAllocationBlock = false;
HasInlineAsmWithSPAdjust = false;
HasOpaqueSPAdjustment = false;
HasVAStart = false;
HasMustTailInVarArgFunc = false;
Save = nullptr;
Expand Down Expand Up @@ -468,9 +468,9 @@ class MachineFrameInfo {
bool hasCalls() const { return HasCalls; }
void setHasCalls(bool V) { HasCalls = V; }

/// Returns true if the function contains any stack-adjusting inline assembly.
bool hasInlineAsmWithSPAdjust() const { return HasInlineAsmWithSPAdjust; }
void setHasInlineAsmWithSPAdjust(bool B) { HasInlineAsmWithSPAdjust = B; }
/// Returns true if the function contains opaque dynamic stack adjustments.
bool hasOpaqueSPAdjustment() const { return HasOpaqueSPAdjustment; }
void setHasOpaqueSPAdjustment(bool B) { HasOpaqueSPAdjustment = B; }

/// Returns true if the function calls the llvm.va_start intrinsic.
bool hasVAStart() const { return HasVAStart; }
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp
Expand Up @@ -148,7 +148,7 @@ void FunctionLoweringInfo::set(const Function &fn, MachineFunction &mf,
TLI->getRegForInlineAsmConstraint(TRI, Op.ConstraintCode,
Op.ConstraintVT);
if (PhysReg.first == SP)
MF->getFrameInfo()->setHasInlineAsmWithSPAdjust(true);
MF->getFrameInfo()->setHasOpaqueSPAdjustment(true);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Expand Up @@ -796,7 +796,7 @@ void RegsForValue::AddInlineAsmOperands(unsigned Code, bool HasMatching,
if (TheReg == SP && Code == InlineAsm::Kind_Clobber) {
// If we clobbered the stack pointer, MFI should know about it.
assert(DAG.getMachineFunction().getFrameInfo()->
hasInlineAsmWithSPAdjust());
hasOpaqueSPAdjustment());
}
}
}
Expand Down
17 changes: 15 additions & 2 deletions llvm/lib/Target/X86/X86FrameLowering.cpp
Expand Up @@ -90,7 +90,7 @@ bool X86FrameLowering::hasFP(const MachineFunction &MF) const {
return (MF.getTarget().Options.DisableFramePointerElim(MF) ||
TRI->needsStackRealignment(MF) ||
MFI->hasVarSizedObjects() ||
MFI->isFrameAddressTaken() || MFI->hasInlineAsmWithSPAdjust() ||
MFI->isFrameAddressTaken() || MFI->hasOpaqueSPAdjustment() ||
MF.getInfo<X86MachineFunctionInfo>()->getForceFramePointer() ||
MMI.callsUnwindInit() || MMI.callsEHReturn() ||
MFI->hasStackMap() || MFI->hasPatchPoint());
Expand Down Expand Up @@ -967,13 +967,26 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
.addReg(StackPtr)
.setMIFlag(MachineInstr::FrameSetup);
if (X86FI->getRestoreBasePointer()) {
// Stash value of base pointer. Saving RSP instead of EBP shortens dependence chain.
// Stash value of base pointer. Saving RSP instead of EBP shortens
// dependence chain. Used by SjLj EH.
unsigned Opm = Uses64BitFramePtr ? X86::MOV64mr : X86::MOV32mr;
addRegOffset(BuildMI(MBB, MBBI, DL, TII.get(Opm)),
FramePtr, true, X86FI->getRestoreBasePointerOffset())
.addReg(StackPtr)
.setMIFlag(MachineInstr::FrameSetup);
}

if (X86FI->getHasSEHFramePtrSave()) {
// Stash the value of the frame pointer relative to the base pointer for
// Win32 EH. This supports Win32 EH, which does the inverse of the above:
// it recovers the frame pointer from the base pointer rather than the
// other way around.
unsigned Opm = Uses64BitFramePtr ? X86::MOV64mr : X86::MOV32mr;
addRegOffset(BuildMI(MBB, MBBI, DL, TII.get(Opm)), BasePtr, true,
getFrameIndexOffset(MF, X86FI->getSEHFramePtrSaveIndex()))
.addReg(FramePtr)
.setMIFlag(MachineInstr::FrameSetup);
}
}

if (((!HasFP && NumBytes) || PushedRegs) && NeedsDwarfCFI) {
Expand Down
90 changes: 62 additions & 28 deletions llvm/lib/Target/X86/X86ISelLowering.cpp
Expand Up @@ -2558,7 +2558,7 @@ X86TargetLowering::LowerFormalArguments(SDValue Chain,

MachineModuleInfo &MMI = MF.getMMI();
const Function *WinEHParent = nullptr;
if (IsWin64 && MMI.hasWinEHFuncInfo(Fn))
if (MMI.hasWinEHFuncInfo(Fn))
WinEHParent = MMI.getWinEHParent(Fn);
bool IsWinEHOutlined = WinEHParent && WinEHParent != Fn;
bool IsWinEHParent = WinEHParent && WinEHParent == Fn;
Expand Down Expand Up @@ -2651,7 +2651,7 @@ X86TargetLowering::LowerFormalArguments(SDValue Chain,

if (!MemOps.empty())
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOps);
} else if (IsWinEHOutlined) {
} else if (IsWin64 && IsWinEHOutlined) {
// Get to the caller-allocated home save location. Add 8 to account
// for the return address.
int HomeOffset = TFI.getOffsetOfLocalArea() + 8;
Expand Down Expand Up @@ -2739,14 +2739,21 @@ X86TargetLowering::LowerFormalArguments(SDValue Chain,
FuncInfo->setArgumentStackSize(StackSize);

if (IsWinEHParent) {
int UnwindHelpFI = MFI->CreateStackObject(8, 8, /*isSS=*/false);
SDValue StackSlot = DAG.getFrameIndex(UnwindHelpFI, MVT::i64);
MMI.getWinEHFuncInfo(MF.getFunction()).UnwindHelpFrameIdx = UnwindHelpFI;
SDValue Neg2 = DAG.getConstant(-2, dl, MVT::i64);
Chain = DAG.getStore(Chain, dl, Neg2, StackSlot,
MachinePointerInfo::getFixedStack(UnwindHelpFI),
/*isVolatile=*/true,
/*isNonTemporal=*/false, /*Alignment=*/0);
if (Is64Bit) {
int UnwindHelpFI = MFI->CreateStackObject(8, 8, /*isSS=*/false);
SDValue StackSlot = DAG.getFrameIndex(UnwindHelpFI, MVT::i64);
MMI.getWinEHFuncInfo(MF.getFunction()).UnwindHelpFrameIdx = UnwindHelpFI;
SDValue Neg2 = DAG.getConstant(-2, dl, MVT::i64);
Chain = DAG.getStore(Chain, dl, Neg2, StackSlot,
MachinePointerInfo::getFixedStack(UnwindHelpFI),
/*isVolatile=*/true,
/*isNonTemporal=*/false, /*Alignment=*/0);
} else {
// Functions using Win32 EH are considered to have opaque SP adjustments
// to force local variables to be addressed from the frame or base
// pointers.
MFI->setHasOpaqueSPAdjustment(true);
}
}

return Chain;
Expand Down Expand Up @@ -15230,6 +15237,20 @@ static SDValue getScalarMaskingNode(SDValue Op, SDValue Mask,
return DAG.getNode(X86ISD::SELECT, dl, VT, IMask, Op, PreservedSrc);
}

static int getSEHRegistrationNodeSize(const Function *Fn) {
if (!Fn->hasPersonalityFn())
report_fatal_error(
"querying registration node size for function without personality");
// The RegNodeSize is 6 32-bit words for SEH and 4 for C++ EH. See
// WinEHStatePass for the full struct definition.
switch (classifyEHPersonality(Fn->getPersonalityFn())) {
case EHPersonality::MSVC_X86SEH: return 24;
case EHPersonality::MSVC_CXX: return 16;
default: break;
}
report_fatal_error("can only recover FP for MSVC EH personality functions");
}

/// When the 32-bit MSVC runtime transfers control to us, either to an outlined
/// function or when returning to a parent frame after catching an exception, we
/// recover the parent frame pointer by doing arithmetic on the incoming EBP.
Expand All @@ -15252,15 +15273,7 @@ static SDValue recoverFramePointer(SelectionDAG &DAG, const Function *Fn,
if (!Fn->hasPersonalityFn())
return EntryEBP;

// The RegNodeSize is 6 32-bit words for SEH and 4 for C++ EH. See
// WinEHStatePass for the full struct definition.
int RegNodeSize;
switch (classifyEHPersonality(Fn->getPersonalityFn())) {
default:
report_fatal_error("can only recover FP for MSVC EH personality functions");
case EHPersonality::MSVC_X86SEH: RegNodeSize = 24; break;
case EHPersonality::MSVC_CXX: RegNodeSize = 16; break;
}
int RegNodeSize = getSEHRegistrationNodeSize(Fn);

// Get an MCSymbol that will ultimately resolve to the frame offset of the EH
// registration.
Expand Down Expand Up @@ -15963,6 +15976,7 @@ static SDValue LowerREADCYCLECOUNTER(SDValue Op, const X86Subtarget *Subtarget,
static SDValue LowerSEHRESTOREFRAME(SDValue Op, const X86Subtarget *Subtarget,
SelectionDAG &DAG) {
MachineFunction &MF = DAG.getMachineFunction();
const Function *Fn = MF.getFunction();
SDLoc dl(Op);
SDValue Chain = Op.getOperand(0);

Expand All @@ -15976,26 +15990,46 @@ static SDValue LowerSEHRESTOREFRAME(SDValue Op, const X86Subtarget *Subtarget,
unsigned FrameReg =
RegInfo->getPtrSizedFrameRegister(DAG.getMachineFunction());
unsigned SPReg = RegInfo->getStackRegister();
unsigned SlotSize = RegInfo->getSlotSize();

// Get incoming EBP.
SDValue IncomingEBP =
DAG.getCopyFromReg(Chain, dl, FrameReg, VT);

// Load [EBP-24] into SP.
SDValue SPAddr =
DAG.getNode(ISD::ADD, dl, VT, IncomingEBP, DAG.getConstant(-24, dl, VT));
// SP is saved in the first field of every registration node, so load
// [EBP-RegNodeSize] into SP.
int RegNodeSize = getSEHRegistrationNodeSize(Fn);
SDValue SPAddr = DAG.getNode(ISD::ADD, dl, VT, IncomingEBP,
DAG.getConstant(-RegNodeSize, dl, VT));
SDValue NewSP =
DAG.getLoad(VT, dl, Chain, SPAddr, MachinePointerInfo(), false, false,
false, VT.getScalarSizeInBits() / 8);
Chain = DAG.getCopyToReg(Chain, dl, SPReg, NewSP);

// FIXME: Restore the base pointer in case of stack realignment!
if (RegInfo->needsStackRealignment(MF))
report_fatal_error("SEH with stack realignment not yet implemented");
if (!RegInfo->needsStackRealignment(MF)) {
// Adjust EBP to point back to the original frame position.
SDValue NewFP = recoverFramePointer(DAG, Fn, IncomingEBP);
Chain = DAG.getCopyToReg(Chain, dl, FrameReg, NewFP);
} else {
assert(RegInfo->hasBasePointer(MF) &&
"functions with Win32 EH must use frame or base pointer register");

// Reload the base pointer (ESI) with the adjusted incoming EBP.
SDValue NewBP = recoverFramePointer(DAG, Fn, IncomingEBP);
Chain = DAG.getCopyToReg(Chain, dl, RegInfo->getBaseRegister(), NewBP);

// Reload the spilled EBP value, now that the stack and base pointers are
// set up.
X86MachineFunctionInfo *X86FI = MF.getInfo<X86MachineFunctionInfo>();
X86FI->setHasSEHFramePtrSave(true);
int FI = MF.getFrameInfo()->CreateSpillStackObject(SlotSize, SlotSize);
X86FI->setSEHFramePtrSaveIndex(FI);
SDValue NewFP = DAG.getLoad(VT, dl, Chain, DAG.getFrameIndex(FI, VT),
MachinePointerInfo(), false, false, false,
VT.getScalarSizeInBits() / 8);
Chain = DAG.getCopyToReg(NewFP, dl, FrameReg, NewFP);
}

// Adjust EBP to point back to the original frame position.
SDValue NewFP = recoverFramePointer(DAG, MF.getFunction(), IncomingEBP);
Chain = DAG.getCopyToReg(Chain, dl, FrameReg, NewFP);
return Chain;
}

Expand Down
14 changes: 14 additions & 0 deletions llvm/lib/Target/X86/X86MachineFunctionInfo.h
Expand Up @@ -84,6 +84,14 @@ class X86MachineFunctionInfo : public MachineFunctionInfo {
/// of pushes to pass function parameters.
bool HasPushSequences = false;

/// True if the function uses llvm.x86.seh.restoreframe, and it needed a spill
/// slot for the frame pointer.
bool HasSEHFramePtrSave = false;

/// The frame index of a stack object containing the original frame pointer
/// used to address arguments in a function using a base pointer.
int SEHFramePtrSaveIndex = 0;

private:
/// ForwardedMustTailRegParms - A list of virtual and physical registers
/// that must be forwarded to every musttail call.
Expand Down Expand Up @@ -143,6 +151,12 @@ class X86MachineFunctionInfo : public MachineFunctionInfo {
unsigned getNumLocalDynamicTLSAccesses() const { return NumLocalDynamics; }
void incNumLocalDynamicTLSAccesses() { ++NumLocalDynamics; }

bool getHasSEHFramePtrSave() const { return HasSEHFramePtrSave; }
void setHasSEHFramePtrSave(bool V) { HasSEHFramePtrSave = V; }

int getSEHFramePtrSaveIndex() const { return SEHFramePtrSaveIndex; }
void setSEHFramePtrSaveIndex(int Index) { SEHFramePtrSaveIndex = Index; }

SmallVectorImpl<ForwardedRegister> &getForwardedMustTailRegParms() {
return ForwardedMustTailRegParms;
}
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Target/X86/X86RegisterInfo.cpp
Expand Up @@ -452,7 +452,7 @@ bool X86RegisterInfo::hasBasePointer(const MachineFunction &MF) const {
// use both the SP and the FP, we need a separate base pointer register.
bool CantUseFP = needsStackRealignment(MF);
bool CantUseSP =
MFI->hasVarSizedObjects() || MFI->hasInlineAsmWithSPAdjust();
MFI->hasVarSizedObjects() || MFI->hasOpaqueSPAdjustment();
return CantUseFP && CantUseSP;
}

Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Target/X86/X86SelectionDAGInfo.cpp
Expand Up @@ -37,7 +37,7 @@ bool X86SelectionDAGInfo::isBaseRegConflictPossible(
// dynamic stack adjustments (hopefully rare) and the base pointer would
// conflict if we had to use it.
MachineFrameInfo *MFI = DAG.getMachineFunction().getFrameInfo();
if (!MFI->hasVarSizedObjects() && !MFI->hasInlineAsmWithSPAdjust())
if (!MFI->hasVarSizedObjects() && !MFI->hasOpaqueSPAdjustment())
return false;

const X86RegisterInfo *TRI = static_cast<const X86RegisterInfo *>(
Expand Down
99 changes: 99 additions & 0 deletions llvm/test/CodeGen/X86/seh-stack-realign-win32.ll
@@ -0,0 +1,99 @@
; RUN: llc -mtriple=i686-windows-msvc < %s | FileCheck %s

; 32-bit catch-all has to use a filter function because that's how it saves the
; exception code.

@str = linkonce_odr unnamed_addr constant [27 x i8] c"GetExceptionCode(): 0x%lx\0A\00", align 1

declare i32 @_except_handler3(...)
declare void @crash()
declare i32 @printf(i8* nocapture readonly, ...) nounwind
declare i32 @llvm.eh.typeid.for(i8*)
declare i8* @llvm.frameaddress(i32)
declare i8* @llvm.localrecover(i8*, i8*, i32)
declare void @llvm.localescape(...)
declare i8* @llvm.x86.seh.recoverfp(i8*, i8*)

define i32 @main() personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) {
entry:
; The EH code allocation is overaligned, triggering realignment.
%__exceptioncode = alloca i32, align 8
call void (...) @llvm.localescape(i32* %__exceptioncode)
invoke void @crash() #5
to label %__try.cont unwind label %lpad

lpad: ; preds = %entry
%0 = landingpad { i8*, i32 }
catch i8* bitcast (i32 ()* @"filt$main" to i8*)
%1 = extractvalue { i8*, i32 } %0, 1
%2 = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @"filt$main" to i8*)) #4
%matches = icmp eq i32 %1, %2
br i1 %matches, label %__except, label %eh.resume

__except: ; preds = %lpad
%3 = load i32, i32* %__exceptioncode, align 4
%call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([27 x i8], [27 x i8]* @str, i32 0, i32 0), i32 %3) #4
br label %__try.cont

__try.cont: ; preds = %entry, %__except
ret i32 0

eh.resume: ; preds = %lpad
resume { i8*, i32 } %0
}

define internal i32 @"filt$main"() {
entry:
%ebp = tail call i8* @llvm.frameaddress(i32 1)
%parentfp = tail call i8* @llvm.x86.seh.recoverfp(i8* bitcast (i32 ()* @main to i8*), i8* %ebp)
%code.i8 = tail call i8* @llvm.localrecover(i8* bitcast (i32 ()* @main to i8*), i8* %parentfp, i32 0)
%__exceptioncode = bitcast i8* %code.i8 to i32*
%info.addr = getelementptr inbounds i8, i8* %ebp, i32 -20
%0 = bitcast i8* %info.addr to i32***
%1 = load i32**, i32*** %0, align 4
%2 = load i32*, i32** %1, align 4
%3 = load i32, i32* %2, align 4
store i32 %3, i32* %__exceptioncode, align 4
ret i32 1
}

; Check that we can get the exception code from eax to the printf.

; CHECK-LABEL: _main:
; CHECK: Lmain$frame_escape_0 = [[code_offs:[-0-9]+]]
; CHECK: Lmain$frame_escape_1 = [[reg_offs:[-0-9]+]]
; CHECK: movl %esp, [[reg_offs]](%esi)
; CHECK: movl $L__ehtable$main,
; EH state 0
; CHECK: movl $0, 40(%esi)
; CHECK: calll _crash
; CHECK: retl
; CHECK: # Block address taken
; stackrestore
; CHECK: movl -24(%ebp), %esp
; CHECK: movl $Lmain$parent_frame_offset, %eax
; CHECK: negl %eax
; CHECK: leal -24(%ebp,%eax), %esi
; CHECK: movl 12(%esi), %ebp # 4-byte Reload
; EH state -1
; CHECK: movl [[code_offs]](%esi), %[[code:[a-z]+]]
; CHECK: movl $-1, 40(%esi)
; CHECK-DAG: movl %[[code]], 4(%esp)
; CHECK-DAG: movl $_str, (%esp)
; CHECK: calll _printf

; CHECK: .section .xdata,"dr"
; CHECK: Lmain$parent_frame_offset = Lmain$frame_escape_1
; CHECK: L__ehtable$main
; CHECK-NEXT: .long -1
; CHECK-NEXT: .long _filt$main
; CHECK-NEXT: .long Ltmp{{[0-9]+}}

; CHECK-LABEL: _filt$main:
; CHECK: pushl %ebp
; CHECK: movl %esp, %ebp
; CHECK: movl (%ebp), %[[oldebp:[a-z]+]]
; CHECK: movl -20(%[[oldebp]]), %[[ehinfo:[a-z]+]]
; CHECK: movl (%[[ehinfo]]), %[[ehrec:[a-z]+]]
; CHECK: movl (%[[ehrec]]), %[[ehcode:[a-z]+]]
; CHECK: movl %[[ehcode]], {{.*}}(%{{.*}})

0 comments on commit e69bdb8

Please sign in to comment.