diff --git a/include/llvm/Target/TargetFrameLowering.h b/include/llvm/Target/TargetFrameLowering.h index 9585f7c3498..398c91ef562 100644 --- a/include/llvm/Target/TargetFrameLowering.h +++ b/include/llvm/Target/TargetFrameLowering.h @@ -265,6 +265,10 @@ public: RegScavenger *RS = nullptr) const { } + virtual unsigned getWinEHParentFrameOffset(const MachineFunction &MF) const { + report_fatal_error("WinEH not implemented for this target"); + } + /// eliminateCallFramePseudoInstr - This method is called during prolog/epilog /// code insertion to eliminate call frame setup and destroy pseudo /// instructions (but only if the Target is using them). It is responsible diff --git a/lib/CodeGen/AsmPrinter/WinException.cpp b/lib/CodeGen/AsmPrinter/WinException.cpp index 319320a09e0..fab5b3a59eb 100644 --- a/lib/CodeGen/AsmPrinter/WinException.cpp +++ b/lib/CodeGen/AsmPrinter/WinException.cpp @@ -710,6 +710,13 @@ void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) { OS.EmitValue(create32bitRef(HandlerMapXData), 4); // HandlerArray } + // All funclets use the same parent frame offset currently. + unsigned ParentFrameOffset = 0; + if (shouldEmitPersonality) { + const TargetFrameLowering *TFI = MF->getSubtarget().getFrameLowering(); + ParentFrameOffset = TFI->getWinEHParentFrameOffset(*MF); + } + for (size_t I = 0, E = FuncInfo.TryBlockMap.size(); I != E; ++I) { WinEHTryBlockMapEntry &TBME = FuncInfo.TryBlockMap[I]; MCSymbol *HandlerMapXData = HandlerMaps[I]; @@ -749,13 +756,8 @@ void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) { OS.EmitValue(create32bitRef(HT.TypeDescriptor), 4); // Type OS.EmitValue(FrameAllocOffsetRef, 4); // CatchObjOffset OS.EmitValue(create32bitRef(HandlerSym), 4); // Handler - - if (shouldEmitPersonality) { - // Keep this in sync with X86FrameLowering::emitPrologue. - int ParentFrameOffset = - 16 + 8 + MF->getFrameInfo()->getMaxCallFrameSize(); + if (shouldEmitPersonality) OS.EmitIntValue(ParentFrameOffset, 4); // ParentFrameOffset - } } } } diff --git a/lib/Target/X86/X86FrameLowering.cpp b/lib/Target/X86/X86FrameLowering.cpp index 83a4acd7a4a..b4dccb60ccc 100644 --- a/lib/Target/X86/X86FrameLowering.cpp +++ b/lib/Target/X86/X86FrameLowering.cpp @@ -786,7 +786,7 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, // NumBytes value that we would've used for the parent frame. unsigned ParentFrameNumBytes = NumBytes; if (IsFunclet) - NumBytes = MFI->getMaxCallFrameSize(); + NumBytes = getWinEHFuncletFrameSize(MF); // Skip the callee-saved push instructions. bool PushedRegs = false; @@ -1039,6 +1039,22 @@ static bool isFuncletReturnInstr(MachineInstr *MI) { llvm_unreachable("impossible"); } +unsigned X86FrameLowering::getWinEHFuncletFrameSize(const MachineFunction &MF) const { + // This is the size of the pushed CSRs. + unsigned CSSize = + MF.getInfo()->getCalleeSavedFrameSize(); + // This is the amount of stack a funclet needs to allocate. + unsigned MaxCallSize = MF.getFrameInfo()->getMaxCallFrameSize(); + // RBP is not included in the callee saved register block. After pushing RBP, + // everything is 16 byte aligned. Everything we allocate before an outgoing + // call must also be 16 byte aligned. + unsigned FrameSizeMinusRBP = + RoundUpToAlignment(CSSize + MaxCallSize, getStackAlignment()); + // Subtract out the size of the callee saved registers. This is how much stack + // each funclet will allocate. + return FrameSizeMinusRBP - CSSize; +} + void X86FrameLowering::emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const { const MachineFrameInfo *MFI = MF.getFrameInfo(); @@ -1067,7 +1083,7 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF, uint64_t NumBytes = 0; if (MBBI->getOpcode() == X86::CATCHRET) { - NumBytes = MFI->getMaxCallFrameSize(); + NumBytes = getWinEHFuncletFrameSize(MF); assert(hasFP(MF) && "EH funclets without FP not yet implemented"); MachineBasicBlock *TargetMBB = MBBI->getOperand(0).getMBB(); @@ -1107,7 +1123,7 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF, .addMBB(TargetMBB); } } else if (MBBI->getOpcode() == X86::CLEANUPRET) { - NumBytes = MFI->getMaxCallFrameSize(); + NumBytes = getWinEHFuncletFrameSize(MF); assert(hasFP(MF) && "EH funclets without FP not yet implemented"); BuildMI(MBB, MBBI, DL, TII.get(Is64Bit ? X86::POP64r : X86::POP32r), MachineFramePtr) @@ -2211,3 +2227,15 @@ MachineBasicBlock::iterator X86FrameLowering::restoreWin32EHStackPointers( } return MBBI; } + +unsigned X86FrameLowering::getWinEHParentFrameOffset(const MachineFunction &MF) const { + // RDX, the parent frame pointer, is homed into 16(%rsp) in the prologue. + unsigned Offset = 16; + // RBP is immediately pushed. + Offset += SlotSize; + // All callee-saved registers are then pushed. + Offset += MF.getInfo()->getCalleeSavedFrameSize(); + // Every funclet allocates enough stack space for the largest outgoing call. + Offset += getWinEHFuncletFrameSize(MF); + return Offset; +} diff --git a/lib/Target/X86/X86FrameLowering.h b/lib/Target/X86/X86FrameLowering.h index b9cf3a99bb3..59c6a062810 100644 --- a/lib/Target/X86/X86FrameLowering.h +++ b/lib/Target/X86/X86FrameLowering.h @@ -101,6 +101,8 @@ public: MachineBasicBlock &MBB, MachineBasicBlock::iterator MI) const override; + unsigned getWinEHParentFrameOffset(const MachineFunction &MF) const override; + /// Check the instruction before/after the passed instruction. If /// it is an ADD/SUB/LEA instruction it is deleted argument and the /// stack adjustment is returned as a positive value for ADD/LEA and @@ -152,6 +154,8 @@ private: restoreWin32EHStackPointers(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, DebugLoc DL, bool RestoreSP = false) const; + + unsigned getWinEHFuncletFrameSize(const MachineFunction &MF) const; }; } // End llvm namespace diff --git a/test/CodeGen/X86/win-catchpad-csrs.ll b/test/CodeGen/X86/win-catchpad-csrs.ll index 5e8c3350bca..32031e2787e 100644 --- a/test/CodeGen/X86/win-catchpad-csrs.ll +++ b/test/CodeGen/X86/win-catchpad-csrs.ll @@ -125,14 +125,20 @@ catchendblock: ; preds = %catch, ; X64: .seh_pushreg 5 ; X64: pushq %rsi ; X64: .seh_pushreg 6 -; X64: subq $32, %rsp -; X64: .seh_stackalloc 32 +; X64: pushq %rdi +; X64: .seh_pushreg 7 +; X64: pushq %rbx +; X64: .seh_pushreg 3 +; X64: subq $40, %rsp +; X64: .seh_stackalloc 40 ; X64: leaq 32(%rdx), %rbp ; X64: .seh_endprologue ; X64: movl $2, %ecx ; X64: callq f ; X64: leaq [[contbb]](%rip), %rax -; X64: addq $32, %rsp +; X64: addq $40, %rsp +; X64: popq %rbx +; X64: popq %rdi ; X64: popq %rsi ; X64: popq %rbp ; X64: retq @@ -142,4 +148,130 @@ catchendblock: ; preds = %catch, ; X64: .long "??_R0H@8"@IMGREL ; X64: .long 0 ; X64: .long "?catch$[[catch1bb]]@?0?try_catch_catch@4HA"@IMGREL +; X64: .long 88 + +define i32 @try_one_csr() personality i32 (...)* @__CxxFrameHandler3 { +entry: + %a = call i32 @getint() + %b = call i32 @getint() + call void (...) @useints(i32 %a) + invoke void @f(i32 1) + to label %try.cont unwind label %catch.dispatch + +catch.dispatch: ; preds = %entry + %0 = catchpad [%rtti.TypeDescriptor2* @"\01??_R0H@8", i32 0, i8* null] + to label %catch unwind label %catchendblock + +catch: + catchret %0 to label %try.cont + +try.cont: ; preds = %entry, %invoke.cont.2, %invoke.cont.3 + ret i32 0 + +catchendblock: ; preds = %catch, + catchendpad unwind to caller +} + +; X64-LABEL: try_one_csr: +; X64: pushq %rbp +; X64: .seh_pushreg 5 +; X64: pushq %rsi +; X64: .seh_pushreg 6 +; X64-NOT: pushq +; X64: subq $40, %rsp +; X64: .seh_stackalloc 40 +; X64: leaq 32(%rsp), %rbp +; X64: .seh_setframe 5, 32 +; X64: .seh_endprologue +; X64: callq getint +; X64: callq getint +; X64: callq useints +; X64: movl $1, %ecx +; X64: callq f +; X64: [[contbb:\.LBB1_[0-9]+]]: # %try.cont +; X64: addq $40, %rsp +; X64-NOT: popq +; X64: popq %rsi +; X64: popq %rbp +; X64: retq + +; X64: "?catch$[[catch1bb:[0-9]+]]@?0?try_one_csr@4HA": +; X64: LBB1_[[catch1bb]]: # %catch.dispatch{{$}} +; X64: movq %rdx, 16(%rsp) +; X64: pushq %rbp +; X64: .seh_pushreg 5 +; X64: pushq %rsi +; X64: .seh_pushreg 6 +; X64: subq $40, %rsp +; X64: .seh_stackalloc 40 +; X64: leaq 32(%rdx), %rbp +; X64: .seh_endprologue +; X64: leaq [[contbb]](%rip), %rax +; X64: addq $40, %rsp +; X64: popq %rsi +; X64: popq %rbp +; X64: retq + +; X64: $handlerMap$0$try_one_csr: +; X64: .long 0 +; X64: .long "??_R0H@8"@IMGREL +; X64: .long 0 +; X64: .long "?catch$[[catch1bb]]@?0?try_one_csr@4HA"@IMGREL +; X64: .long 72 + +define i32 @try_no_csr() personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @f(i32 1) + to label %try.cont unwind label %catch.dispatch + +catch.dispatch: ; preds = %entry + %0 = catchpad [%rtti.TypeDescriptor2* @"\01??_R0H@8", i32 0, i8* null] + to label %catch unwind label %catchendblock + +catch: + catchret %0 to label %try.cont + +try.cont: ; preds = %entry, %invoke.cont.2, %invoke.cont.3 + ret i32 0 + +catchendblock: ; preds = %catch, + catchendpad unwind to caller +} + +; X64-LABEL: try_no_csr: +; X64: pushq %rbp +; X64: .seh_pushreg 5 +; X64-NOT: pushq +; X64: subq $48, %rsp +; X64: .seh_stackalloc 48 +; X64: leaq 48(%rsp), %rbp +; X64: .seh_setframe 5, 48 +; X64: .seh_endprologue +; X64: movl $1, %ecx +; X64: callq f +; X64: [[contbb:\.LBB2_[0-9]+]]: # %try.cont +; X64: addq $48, %rsp +; X64-NOT: popq +; X64: popq %rbp +; X64: retq + +; X64: "?catch$[[catch1bb:[0-9]+]]@?0?try_no_csr@4HA": +; X64: LBB2_[[catch1bb]]: # %catch.dispatch{{$}} +; X64: movq %rdx, 16(%rsp) +; X64: pushq %rbp +; X64: .seh_pushreg 5 +; X64: subq $32, %rsp +; X64: .seh_stackalloc 32 +; X64: leaq 48(%rdx), %rbp +; X64: .seh_endprologue +; X64: leaq [[contbb]](%rip), %rax +; X64: addq $32, %rsp +; X64: popq %rbp +; X64: retq + +; X64: $handlerMap$0$try_no_csr: +; X64: .long 0 +; X64: .long "??_R0H@8"@IMGREL +; X64: .long 0 +; X64: .long "?catch$[[catch1bb]]@?0?try_no_csr@4HA"@IMGREL ; X64: .long 56