Index: include/llvm/CodeGen/WinEHFuncInfo.h =================================================================== --- include/llvm/CodeGen/WinEHFuncInfo.h +++ include/llvm/CodeGen/WinEHFuncInfo.h @@ -96,6 +96,7 @@ SmallVector SEHUnwindMap; SmallVector ClrEHUnwindMap; int UnwindHelpFrameIdx = INT_MAX; + int PSPSymFrameIdx = INT_MAX; int getLastStateNumber() const { return CxxUnwindMap.size() - 1; } Index: lib/Target/X86/X86FrameLowering.h =================================================================== --- lib/Target/X86/X86FrameLowering.h +++ lib/Target/X86/X86FrameLowering.h @@ -187,6 +187,8 @@ DebugLoc DL, int64_t Offset, bool InEpilogue) const; + unsigned getPSPSlotOffsetFromSP(const MachineFunction &MF) const; + unsigned getWinEHFuncletFrameSize(const MachineFunction &MF) const; }; Index: lib/Target/X86/X86FrameLowering.cpp =================================================================== --- lib/Target/X86/X86FrameLowering.cpp +++ lib/Target/X86/X86FrameLowering.cpp @@ -900,9 +900,10 @@ uint64_t MaxAlign = calculateMaxStackAlign(MF); // Desired stack alignment. uint64_t StackSize = MFI->getStackSize(); // Number of bytes to allocate. bool IsFunclet = MBB.isEHFuncletEntry(); - bool IsClrFunclet = - IsFunclet && + bool FnHasClrFunclet = + MMI.hasEHFunclets() && classifyEHPersonality(Fn->getPersonalityFn()) == EHPersonality::CoreCLR; + bool IsClrFunclet = IsFunclet && FnHasClrFunclet; bool HasFP = hasFP(MF); bool IsWin64CC = STI.isCallingConvWin64(Fn->getCallingConv()); bool IsWin64Prologue = MF.getTarget().getMCAsmInfo()->usesWindowsCFI(); @@ -1194,7 +1195,36 @@ .setMIFlag(MachineInstr::FrameSetup); int SEHFrameOffset = 0; - unsigned SPOrEstablisher = IsFunclet ? Establisher : StackPtr; + unsigned SPOrEstablisher; + if (IsFunclet) { + if (IsClrFunclet) { + // The establisher parameter passed to a CLR funclet is actually a pointer + // to the (mostly empty) frame of its nearest enclosing funclet; we have + // to find the root function establisher frame by loading the PSPSym from + // the intermediate frame. + unsigned PSPSlotOffset = getPSPSlotOffsetFromSP(MF); + MachinePointerInfo NoInfo; + MBB.addLiveIn(Establisher); + addRegOffset(BuildMI(MBB, MBBI, DL, TII.get(X86::MOV64rm), Establisher), + Establisher, false, PSPSlotOffset) + .addMemOperand(MF.getMachineMemOperand( + NoInfo, MachineMemOperand::MOLoad, SlotSize, SlotSize)); + ; + // Save the root establisher back into the current funclet's (mostly + // empty) frame, in case a sub-funclet or the GC needs it. + addRegOffset(BuildMI(MBB, MBBI, DL, TII.get(X86::MOV64mr)), StackPtr, + false, PSPSlotOffset) + .addReg(Establisher) + .addMemOperand( + MF.getMachineMemOperand(NoInfo, MachineMemOperand::MOStore | + MachineMemOperand::MOVolatile, + SlotSize, SlotSize)); + } + SPOrEstablisher = Establisher; + } else { + SPOrEstablisher = StackPtr; + } + if (IsWin64Prologue && HasFP) { // Set RBP to a small fixed offset from RSP. In the funclet case, we base // this calculation on the incoming establisher, which holds the value of @@ -1243,6 +1273,21 @@ BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_EndPrologue)) .setMIFlag(MachineInstr::FrameSetup); + if (FnHasClrFunclet && !IsFunclet) { + // Save the so-called Initial-SP (i.e. the value of the stack pointer + // immediately after the prolog) into the PSPSlot so that funclets + // and the GC can recover it. + unsigned PSPSlotOffset = getPSPSlotOffsetFromSP(MF); + auto PSPInfo = MachinePointerInfo::getFixedStack( + MF, MF.getMMI().getWinEHFuncInfo(Fn).PSPSymFrameIdx); + addRegOffset(BuildMI(MBB, MBBI, DL, TII.get(X86::MOV64mr)), StackPtr, false, + PSPSlotOffset) + .addReg(StackPtr) + .addMemOperand(MF.getMachineMemOperand( + PSPInfo, MachineMemOperand::MOStore | MachineMemOperand::MOVolatile, + SlotSize, SlotSize)); + } + // Realign stack after we spilled callee-saved registers (so that we'll be // able to calculate their offsets from the frame pointer). // Win64 requires aligning the stack after the prologue. @@ -1328,17 +1373,54 @@ llvm_unreachable("impossible"); } -unsigned X86FrameLowering::getWinEHFuncletFrameSize(const MachineFunction &MF) const { +// CLR funclets use a special "Previous Stack Pointer Symbol" slot on the +// stack. It holds a pointer to the bottom of the root function frame. The +// establisher frame pointer passed to a nested funclet may point to the +// (mostly empty) frame of its parent funclet, but it will need to find +// the frame of the root function to access locals. To facilitate this, +// every funclet copies the pointer to the bottom of the root function +// frame into a PSPSym slot in its own (mostly empty) stack frame. Using the +// same offset for the PSPSym in the root function frame that's used in the +// funclets' frames allows each funclet to dynamically accept any ancestor +// frame as its establisher argument (the runtime doesn't guarantee the +// immediate parent for some reason lost to history), and also allows the GC, +// which uses the PSPSym for some bookkeeping, to find it in any funclet's +// frame with only a single offset reported for the entire method. +unsigned +X86FrameLowering::getPSPSlotOffsetFromSP(const MachineFunction &MF) const { + MachineModuleInfo &MMI = MF.getMMI(); + WinEHFuncInfo &Info = MMI.getWinEHFuncInfo(MF.getFunction()); + // getFrameIndexReferenceFromSP has an out ref parameter for the stack + // pointer register; pass a dummy that we ignore + unsigned SPReg; + int Offset = getFrameIndexReferenceFromSP(MF, Info.PSPSymFrameIdx, SPReg); + assert(Offset >= 0); + return static_cast(Offset); +} + +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(); + unsigned UsedSize; + EHPersonality Personality = + classifyEHPersonality(MF.getFunction()->getPersonalityFn()); + if (Personality == EHPersonality::CoreCLR) { + // CLR funclets need to hold enough space to include the PSPSym, at the + // same offset from the stack pointer (immediately after the prolog) as it + // resides at in the main function. + UsedSize = getPSPSlotOffsetFromSP(MF) + SlotSize; + } else { + // Other funclets just need enough stack for outgoing call arguments. + UsedSize = 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()); + RoundUpToAlignment(CSSize + UsedSize, getStackAlignment()); // Subtract out the size of the callee saved registers. This is how much stack // each funclet will allocate. return FrameSizeMinusRBP - CSSize; Index: lib/Target/X86/X86ISelLowering.cpp =================================================================== --- lib/Target/X86/X86ISelLowering.cpp +++ lib/Target/X86/X86ISelLowering.cpp @@ -2878,18 +2878,34 @@ FuncInfo->setArgumentStackSize(StackSize); - if (MMI.hasWinEHFuncInfo(Fn) && Is64Bit && - classifyEHPersonality(Fn->getPersonalityFn()) == - EHPersonality::MSVC_CXX) { - 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( - DAG.getMachineFunction(), UnwindHelpFI), - /*isVolatile=*/true, - /*isNonTemporal=*/false, /*Alignment=*/0); + if (MMI.hasWinEHFuncInfo(Fn)) { + EHPersonality Personality = classifyEHPersonality(Fn->getPersonalityFn()); + if (Personality == EHPersonality::MSVC_CXX) { + 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( + DAG.getMachineFunction(), UnwindHelpFI), + /*isVolatile=*/true, + /*isNonTemporal=*/false, /*Alignment=*/0); + } + } else if (Personality == EHPersonality::CoreCLR) { + assert(Is64Bit); + // TODO: Add a mechanism to frame lowering that will allow us to indicate + // that we'd prefer this slot be allocated towards the bottom of the frame + // (i.e. near the stack pointer after allocating the frame). Every + // funclet needs a copy of this slot in its (mostly empty) frame, and the + // offset from the bottom of this and each funclet's frame must be the + // same, so the size of funclets' (mostly empty) frames is dictated by + // how far this slot is from the bottom (since they allocate just enough + // space to accomodate holding this slot at the correct offset). + int PSPSymFI = MFI->CreateStackObject(8, 8, /*isSS=*/false); + MMI.getWinEHFuncInfo(MF.getFunction()).PSPSymFrameIdx = PSPSymFI; + } } return Chain; Index: test/CodeGen/WinEH/wineh-coreclr.ll =================================================================== --- test/CodeGen/WinEH/wineh-coreclr.ll +++ test/CodeGen/WinEH/wineh-coreclr.ll @@ -1,4 +1,4 @@ -; RUN: llc -mtriple=x86_64-pc-windows-coreclr < %s | FileCheck %s +; RUN: llc -mtriple=x86_64-pc-windows-coreclr -verify-machineinstrs < %s | FileCheck %s declare void @ProcessCLRException() declare void @f(i32) @@ -32,7 +32,9 @@ define void @test1() personality i8* bitcast (void ()* @ProcessCLRException to i8*) { entry: ; CHECK: # %entry +; CHECK: leaq [[FPOffset:[0-9]+]](%rsp), %rbp ; CHECK: .seh_endprologue +; CHECK: movq %rsp, [[PSPSymOffset:[0-9]+]](%rsp) ; CHECK: [[L_before_f1:.+]]: ; CHECK-NEXT: movl $1, %ecx ; CHECK-NEXT: callq f @@ -52,8 +54,12 @@ %catch1 = catchpad [i32 1] to label %catch1.body unwind label %catch2.pad catch1.body: -; CHECK: leaq {{[0-9]+}}(%rcx), %rbp -; ^ establisher frame pointer passed in rcx +; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]] +; ^ all funclets use the same frame size +; CHECK: movq [[PSPSymOffset]](%rcx), %rcx +; ^ establisher frame pointer passed in rcx +; CHECK: movq %rcx, [[PSPSymOffset]](%rsp) +; CHECK: leaq [[FPOffset]](%rcx), %rbp ; CHECK: .seh_endprologue ; CHECK: movq %rdx, %rcx ; ^ exception pointer passed in rdx @@ -73,8 +79,12 @@ %catch2 = catchpad [i32 2] to label %catch2.body unwind label %catch.end catch2.body: -; CHECK: leaq {{[0-9]+}}(%rcx), %rbp -; ^ establisher frame pointer passed in rcx +; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]] +; ^ all funclets use the same frame size +; CHECK: movq [[PSPSymOffset]](%rcx), %rcx +; ^ establisher frame pointer passed in rcx +; CHECK: movq %rcx, [[PSPSymOffset]](%rsp) +; CHECK: leaq [[FPOffset]](%rcx), %rbp ; CHECK: .seh_endprologue ; CHECK: movq %rdx, %rcx ; ^ exception pointer passed in rdx @@ -98,8 +108,12 @@ fault.pad: ; CHECK: .seh_proc [[L_fault:[^ ]+]] %fault = cleanuppad [i32 undef] -; CHECK: leaq {{[0-9]+}}(%rcx), %rbp -; ^ establisher frame pointer passed in rcx +; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]] +; ^ all funclets use the same frame size +; CHECK: movq [[PSPSymOffset]](%rcx), %rcx +; ^ establisher frame pointer passed in rcx +; CHECK: movq %rcx, [[PSPSymOffset]](%rsp) +; CHECK: leaq [[FPOffset]](%rcx), %rbp ; CHECK: .seh_endprologue ; CHECK: [[L_before_f6:.+]]: ; CHECK-NEXT: movl $6, %ecx @@ -121,8 +135,12 @@ finally.pad: ; CHECK: .seh_proc [[L_finally:[^ ]+]] %finally = cleanuppad [] -; CHECK: leaq {{[0-9]+}}(%rcx), %rbp -; ^ establisher frame pointer passed in rcx +; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]] +; ^ all funclets use the same frame size +; CHECK: movq [[PSPSymOffset]](%rcx), %rcx +; ^ establisher frame pointer passed in rcx +; CHECK: movq %rcx, [[PSPSymOffset]](%rsp) +; CHECK: leaq [[FPOffset]](%rcx), %rbp ; CHECK: .seh_endprologue ; CHECK: [[L_before_f7:.+]]: ; CHECK-NEXT: movl $7, %ecx