// SEH intrinsics for Windows
let TargetPrefix = "x86" in {
def int_x86_seh_lsda : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty], [IntrNoMem]>;
- def int_x86_seh_exceptioninfo : Intrinsic<[llvm_ptr_ty],
- [llvm_ptr_ty, llvm_ptr_ty],
- [IntrReadMem]>;
+
+ // Restores the frame, base, and stack pointers as necessary after recovering
+ // from an exception. Any block resuming control flow in the parent function
+ // should call this before accessing any stack memory.
+ def int_x86_seh_restoreframe : Intrinsic<[], [], []>;
+
+ // Given a pointer to the end of an EH registration object, returns the true
+ // parent frame address that can be used with llvm.framerecover.
+ def int_x86_seh_recoverfp : Intrinsic<[llvm_ptr_ty],
+ [llvm_ptr_ty, llvm_ptr_ty],
+ [IntrNoMem]>;
}
//===----------------------------------------------------------------------===//
return;
} else {
FuncInfoXData = Asm->OutContext.getOrCreateLSDASymbol(ParentLinkageName);
+ emitEHRegistrationOffsetLabel(FuncInfo, ParentLinkageName);
}
MCSymbol *UnwindMapXData = nullptr;
}
}
+void WinException::emitEHRegistrationOffsetLabel(const WinEHFuncInfo &FuncInfo,
+ StringRef FLinkageName) {
+ // Outlined helpers called by the EH runtime need to know the offset of the EH
+ // registration in order to recover the parent frame pointer. Now that we know
+ // we've code generated the parent, we can emit the label assignment that
+ // those helpers use to get the offset of the registration node.
+ assert(FuncInfo.EHRegNodeEscapeIndex != INT_MAX &&
+ "no EH reg node frameescape index");
+ MCSymbol *ParentFrameOffset =
+ Asm->OutContext.getOrCreateParentFrameOffsetSymbol(FLinkageName);
+ MCSymbol *RegistrationOffsetSym = Asm->OutContext.getOrCreateFrameAllocSymbol(
+ FLinkageName, FuncInfo.EHRegNodeEscapeIndex);
+ const MCExpr *RegistrationOffsetSymRef =
+ MCSymbolRefExpr::create(RegistrationOffsetSym, Asm->OutContext);
+ Asm->OutStreamer->EmitAssignment(ParentFrameOffset, RegistrationOffsetSymRef);
+}
+
/// Emit the language-specific data that _except_handler3 and 4 expect. This is
/// functionally equivalent to the __C_specific_handler table, except it is
/// indexed by state number instead of IP.
void WinException::emitExceptHandlerTable(const MachineFunction *MF) {
MCStreamer &OS = *Asm->OutStreamer;
-
- // Define the EH registration node offset label in terms of its frameescape
- // label. The WinEHStatePass ensures that the registration node is passed to
- // frameescape. This allows SEH filter functions to access the
- // EXCEPTION_POINTERS field, which is filled in by the _except_handlerN.
const Function *F = MF->getFunction();
- WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(F);
- assert(FuncInfo.EHRegNodeEscapeIndex != INT_MAX &&
- "no EH reg node frameescape index");
StringRef FLinkageName = GlobalValue::getRealLinkageName(F->getName());
- MCSymbol *ParentFrameOffset =
- Asm->OutContext.getOrCreateParentFrameOffsetSymbol(FLinkageName);
- MCSymbol *FrameAllocSym = Asm->OutContext.getOrCreateFrameAllocSymbol(
- FLinkageName, FuncInfo.EHRegNodeEscapeIndex);
- const MCSymbolRefExpr *FrameAllocSymRef =
- MCSymbolRefExpr::create(FrameAllocSym, Asm->OutContext);
- OS.EmitAssignment(ParentFrameOffset, FrameAllocSymRef);
+
+ WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(F);
+ emitEHRegistrationOffsetLabel(FuncInfo, FLinkageName);
// Emit the __ehtable label that we use for llvm.x86.seh.lsda.
MCSymbol *LSDALabel = Asm->OutContext.getOrCreateLSDASymbol(FLinkageName);
void extendIP2StateTable(const MachineFunction *MF, const Function *ParentF,
WinEHFuncInfo &FuncInfo);
+ /// Emits the label used with llvm.x86.seh.recoverfp, which is used by
+ /// outlined funclets.
+ void emitEHRegistrationOffsetLabel(const WinEHFuncInfo &FuncInfo,
+ StringRef FLinkageName);
+
const MCExpr *create32bitRef(const MCSymbol *Value);
const MCExpr *create32bitRef(const GlobalValue *GV);
return DAG.getNode(X86ISD::SELECT, dl, VT, IMask, Op, PreservedSrc);
}
+/// 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.
+/// Here's the math:
+/// RegNodeBase = EntryEBP - RegNodeSize
+/// ParentFP = RegNodeBase - RegNodeFrameOffset
+/// Subtracting RegNodeSize takes us to the offset of the registration node, and
+/// subtracting the offset (negative on x86) takes us back to the parent FP.
+static SDValue recoverFramePointer(SelectionDAG &DAG, const Function *Fn,
+ SDValue EntryEBP) {
+ MachineFunction &MF = DAG.getMachineFunction();
+ SDLoc dl;
+
+ const TargetLowering &TLI = DAG.getTargetLoweringInfo();
+ MVT PtrVT = TLI.getPointerTy();
+
+ // 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;
+ }
+
+ // Get an MCSymbol that will ultimately resolve to the frame offset of the EH
+ // registration.
+ MCSymbol *OffsetSym =
+ MF.getMMI().getContext().getOrCreateParentFrameOffsetSymbol(
+ GlobalValue::getRealLinkageName(Fn->getName()));
+ SDValue OffsetSymVal = DAG.getMCSymbol(OffsetSym, PtrVT);
+ SDValue RegNodeFrameOffset =
+ DAG.getNode(ISD::FRAME_ALLOC_RECOVER, dl, PtrVT, OffsetSymVal);
+
+ // RegNodeBase = EntryEBP - RegNodeSize
+ // ParentFP = RegNodeBase - RegNodeFrameOffset
+ SDValue RegNodeBase = DAG.getNode(ISD::SUB, dl, PtrVT, EntryEBP,
+ DAG.getConstant(RegNodeSize, dl, PtrVT));
+ return DAG.getNode(ISD::SUB, dl, PtrVT, RegNodeBase, RegNodeFrameOffset);
+}
+
static SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, const X86Subtarget *Subtarget,
SelectionDAG &DAG) {
SDLoc dl(Op);
SDValue Result = DAG.getMCSymbol(LSDASym, VT);
return DAG.getNode(X86ISD::Wrapper, dl, VT, Result);
}
+
+ case Intrinsic::x86_seh_recoverfp: {
+ SDValue FnOp = Op.getOperand(1);
+ SDValue IncomingFPOp = Op.getOperand(2);
+ GlobalAddressSDNode *GSD = dyn_cast<GlobalAddressSDNode>(FnOp);
+ auto *Fn = dyn_cast_or_null<Function>(GSD ? GSD->getGlobal() : nullptr);
+ if (!Fn)
+ report_fatal_error(
+ "llvm.x86.seh.recoverfp must take a function as the first argument");
+ return recoverFramePointer(DAG, Fn, IncomingFPOp);
+ }
}
}
return DAG.getMergeValues(Results, DL);
}
-static SDValue LowerEXCEPTIONINFO(SDValue Op, const X86Subtarget *Subtarget,
- SelectionDAG &DAG) {
+static SDValue LowerSEHRESTOREFRAME(SDValue Op, const X86Subtarget *Subtarget,
+ SelectionDAG &DAG) {
MachineFunction &MF = DAG.getMachineFunction();
SDLoc dl(Op);
- SDValue FnOp = Op.getOperand(2);
- SDValue FPOp = Op.getOperand(3);
+ SDValue Chain = Op.getOperand(0);
- // Compute the symbol for the parent EH registration. We know it'll get
- // emitted later.
- auto *Fn = cast<Function>(cast<GlobalAddressSDNode>(FnOp)->getGlobal());
- MCSymbol *ParentFrameSym =
- MF.getMMI().getContext().getOrCreateParentFrameOffsetSymbol(
- GlobalValue::getRealLinkageName(Fn->getName()));
+ const TargetLowering &TLI = DAG.getTargetLoweringInfo();
+ MVT VT = TLI.getPointerTy();
- // Create a TargetExternalSymbol for the label to avoid any target lowering
- // that would make this PC relative.
- MVT PtrVT = Op.getSimpleValueType();
- SDValue OffsetSym = DAG.getMCSymbol(ParentFrameSym, PtrVT);
- SDValue OffsetVal =
- DAG.getNode(ISD::FRAME_ALLOC_RECOVER, dl, PtrVT, OffsetSym);
+ const X86RegisterInfo *RegInfo = Subtarget->getRegisterInfo();
+ unsigned FrameReg =
+ RegInfo->getPtrSizedFrameRegister(DAG.getMachineFunction());
+ unsigned SPReg = RegInfo->getStackRegister();
- // Add the offset to the FP.
- SDValue Add = DAG.getNode(ISD::ADD, dl, PtrVT, FPOp, OffsetVal);
+ // Get incoming EBP.
+ SDValue IncomingEBP =
+ DAG.getCopyFromReg(Chain, dl, FrameReg, VT);
- // Load the second field of the struct, which is 4 bytes in. See
- // WinEHStatePass for more info.
- Add = DAG.getNode(ISD::ADD, dl, PtrVT, Add, DAG.getConstant(4, dl, PtrVT));
- return DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), Add, MachinePointerInfo(),
- false, false, false, 0);
+ // Load [EBP-24] into SP.
+ SDValue SPAddr =
+ DAG.getNode(ISD::ADD, dl, VT, IncomingEBP, DAG.getConstant(-24, 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!
+
+ // 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;
}
static SDValue LowerINTRINSIC_W_CHAIN(SDValue Op, const X86Subtarget *Subtarget,
const IntrinsicData* IntrData = getIntrinsicWithChain(IntNo);
if (!IntrData) {
- if (IntNo == Intrinsic::x86_seh_exceptioninfo)
- return LowerEXCEPTIONINFO(Op, Subtarget, DAG);
+ if (IntNo == llvm::Intrinsic::x86_seh_restoreframe)
+ return LowerSEHRESTOREFRAME(Op, Subtarget, DAG);
return SDValue();
}
// Set up RegNodeEscapeIndex
int RegNodeEscapeIndex = escapeRegNode(F);
+ FuncInfo.EHRegNodeEscapeIndex = RegNodeEscapeIndex;
// Only insert stores in catch handlers.
Constant *FI8 =
WinEHFuncInfo &FuncInfo = MMI.getWinEHFuncInfo(&F);
// Remember and return the index that we used. We save it in WinEHFuncInfo so
- // that we can lower llvm.x86.seh.exceptioninfo later in filter functions
- // without too much trouble.
+ // that we can lower llvm.x86.seh.recoverfp later in filter functions without
+ // too much trouble.
int RegNodeEscapeIndex = escapeRegNode(F);
FuncInfo.EHRegNodeEscapeIndex = RegNodeEscapeIndex;
}
}
- // Insert llvm.stackrestore into each __except block.
- Function *StackRestore =
- Intrinsic::getDeclaration(TheModule, Intrinsic::stackrestore);
+ // Insert llvm.x86.seh.restoreframe() into each __except block.
+ Function *RestoreFrame =
+ Intrinsic::getDeclaration(TheModule, Intrinsic::x86_seh_restoreframe);
for (BasicBlock *ExceptBB : ExceptBlocks) {
IRBuilder<> Builder(ExceptBB->begin());
- Value *SP =
- Builder.CreateLoad(Builder.CreateStructGEP(RegNodeTy, RegNode, 0));
- Builder.CreateCall(StackRestore, {SP});
+ Builder.CreateCall(RestoreFrame, {});
}
}
declare i8* @llvm.frameaddress(i32)
declare i8* @llvm.framerecover(i8*, i8*, i32)
declare void @llvm.frameescape(...)
-declare i8* @llvm.x86.seh.exceptioninfo(i8*, i8*)
+declare i8* @llvm.x86.seh.recoverfp(i8*, i8*)
define i32 @main() personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) {
entry:
define internal i32 @"filt$main"() {
entry:
- %0 = tail call i8* @llvm.frameaddress(i32 1)
- %1 = tail call i8* @llvm.framerecover(i8* bitcast (i32 ()* @main to i8*), i8* %0, i32 0)
- %__exceptioncode = bitcast i8* %1 to i32*
- %2 = tail call i8* @llvm.x86.seh.exceptioninfo(i8* bitcast (i32 ()* @main to i8*), i8* %0)
- %3 = bitcast i8* %2 to i32**
- %4 = load i32*, i32** %3, align 4
- %5 = load i32, i32* %4, align 4
- store i32 %5, i32* %__exceptioncode, align 4
+ %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.framerecover(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: 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: movl
+; 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]], {{.*}}(%{{.*}})
; ...
; } EXCEPTION_RECORD;
-; FIXME: Use llvm.eh.exceptioninfo for this.
-declare i32 @safe_div_filt0()
-declare i32 @safe_div_filt1()
-; define i32 @safe_div_filt0() {
-; %eh_ptrs_c = bitcast i8* %eh_ptrs to i32**
-; %eh_rec = load i32*, i32** %eh_ptrs_c
-; %eh_code = load i32, i32* %eh_rec
-; ; EXCEPTION_ACCESS_VIOLATION = 0xC0000005
-; %cmp = icmp eq i32 %eh_code, 3221225477
-; %filt.res = zext i1 %cmp to i32
-; ret i32 %filt.res
-; }
-; define i32 @safe_div_filt1() {
-; %eh_ptrs_c = bitcast i8* %eh_ptrs to i32**
-; %eh_rec = load i32*, i32** %eh_ptrs_c
-; %eh_code = load i32, i32* %eh_rec
-; ; EXCEPTION_INT_DIVIDE_BY_ZERO = 0xC0000094
-; %cmp = icmp eq i32 %eh_code, 3221225620
-; %filt.res = zext i1 %cmp to i32
-; ret i32 %filt.res
-; }
+define i32 @safe_div_filt0() {
+ %ebp = call i8* @llvm.frameaddress(i32 1)
+ %eh_ptrs.addr.i8 = getelementptr inbounds i8, i8* %ebp, i32 -20
+ %eh_ptrs.addr = bitcast i8* %eh_ptrs.addr.i8 to i32***
+ %eh_ptrs = load i32**, i32*** %eh_ptrs.addr
+ %eh_rec = load i32*, i32** %eh_ptrs
+ %eh_code = load i32, i32* %eh_rec
+ ; EXCEPTION_ACCESS_VIOLATION = 0xC0000005
+ %cmp = icmp eq i32 %eh_code, 3221225477
+ %filt.res = zext i1 %cmp to i32
+ ret i32 %filt.res
+}
+define i32 @safe_div_filt1() {
+ %ebp = call i8* @llvm.frameaddress(i32 1)
+ %eh_ptrs.addr.i8 = getelementptr inbounds i8, i8* %ebp, i32 -20
+ %eh_ptrs.addr = bitcast i8* %eh_ptrs.addr.i8 to i32***
+ %eh_ptrs = load i32**, i32*** %eh_ptrs.addr
+ %eh_rec = load i32*, i32** %eh_ptrs
+ %eh_code = load i32, i32* %eh_rec
+ ; EXCEPTION_INT_DIVIDE_BY_ZERO = 0xC0000094
+ %cmp = icmp eq i32 %eh_code, 3221225620
+ %filt.res = zext i1 %cmp to i32
+ ret i32 %filt.res
+}
@str_result = internal constant [21 x i8] c"safe_div result: %d\0A\00"
declare void @puts(i8*)
declare void @printf(i8*, ...)
declare void @abort()
+declare i8* @llvm.frameaddress(i32)