From a736fb54210c4f1f161a27e7149e4909589bd821 Mon Sep 17 00:00:00 2001 From: Joseph Tremoulet Date: Fri, 13 Nov 2015 00:39:23 +0000 Subject: [PATCH] [WinEH] Find root frame correctly in CLR funclets Summary: The value that the CoreCLR personality passes to a funclet for the establisher frame may be the root function's frame or may be the parent funclet's (mostly empty) frame in the case of nested funclets. Each funclet stores a pointer to the root frame in its own (mostly empty) frame, as does the root function itself. All frames allocate this slot at the same offset, measured from the post-prolog stack pointer, so that the same sequence can accept any ancestor as an establisher frame parameter value, and so that a single offset can be reported to the GC, which also looks at this slot. This change allocate the slot when processing function entry, and records its frame index on the WinEHFuncInfo object, then inserts the code to set/copy it during prolog emission. Reviewers: majnemer, AndyAyers, pgavlin, rnk Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D14614 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@252983 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/CodeGen/WinEHFuncInfo.h | 1 + lib/Target/X86/X86FrameLowering.cpp | 94 +++++++++++++++++++++++++++++++++--- lib/Target/X86/X86FrameLowering.h | 2 + lib/Target/X86/X86ISelLowering.cpp | 40 ++++++++++----- test/CodeGen/WinEH/wineh-coreclr.ll | 36 ++++++++++---- 5 files changed, 146 insertions(+), 27 deletions(-) diff --git a/include/llvm/CodeGen/WinEHFuncInfo.h b/include/llvm/CodeGen/WinEHFuncInfo.h index 2754de0bf54..782119e84e2 100644 --- a/include/llvm/CodeGen/WinEHFuncInfo.h +++ b/include/llvm/CodeGen/WinEHFuncInfo.h @@ -96,6 +96,7 @@ struct WinEHFuncInfo { SmallVector SEHUnwindMap; SmallVector ClrEHUnwindMap; int UnwindHelpFrameIdx = INT_MAX; + int PSPSymFrameIdx = INT_MAX; int getLastStateNumber() const { return CxxUnwindMap.size() - 1; } diff --git a/lib/Target/X86/X86FrameLowering.cpp b/lib/Target/X86/X86FrameLowering.cpp index 7d257ee6afa..da48a871930 100644 --- a/lib/Target/X86/X86FrameLowering.cpp +++ b/lib/Target/X86/X86FrameLowering.cpp @@ -900,9 +900,10 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, 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 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, .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 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, 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 @@ static bool isFuncletReturnInstr(MachineInstr *MI) { 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; diff --git a/lib/Target/X86/X86FrameLowering.h b/lib/Target/X86/X86FrameLowering.h index db32be12924..753d155657d 100644 --- a/lib/Target/X86/X86FrameLowering.h +++ b/lib/Target/X86/X86FrameLowering.h @@ -187,6 +187,8 @@ private: DebugLoc DL, int64_t Offset, bool InEpilogue) const; + unsigned getPSPSlotOffsetFromSP(const MachineFunction &MF) const; + unsigned getWinEHFuncletFrameSize(const MachineFunction &MF) const; }; diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp index 46b2b5e669f..fdb716ebca4 100644 --- a/lib/Target/X86/X86ISelLowering.cpp +++ b/lib/Target/X86/X86ISelLowering.cpp @@ -2878,18 +2878,34 @@ SDValue X86TargetLowering::LowerFormalArguments( 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; diff --git a/test/CodeGen/WinEH/wineh-coreclr.ll b/test/CodeGen/WinEH/wineh-coreclr.ll index 923d966a5f6..079993f74db 100644 --- a/test/CodeGen/WinEH/wineh-coreclr.ll +++ b/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 @@ declare i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token) 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.pad: %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.pad: %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 @@ try_in_catch: 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.clone: 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 -- 2.11.0