[WinEH] Find root frame correctly in CLR funclets
authorJoseph Tremoulet <jotrem@microsoft.com>
Fri, 13 Nov 2015 00:39:23 +0000 (00:39 +0000)
committerJoseph Tremoulet <jotrem@microsoft.com>
Fri, 13 Nov 2015 00:39:23 +0000 (00:39 +0000)
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
lib/Target/X86/X86FrameLowering.cpp
lib/Target/X86/X86FrameLowering.h
lib/Target/X86/X86ISelLowering.cpp
test/CodeGen/WinEH/wineh-coreclr.ll

index 2754de0..782119e 100644 (file)
@@ -96,6 +96,7 @@ struct WinEHFuncInfo {
   SmallVector<SEHUnwindMapEntry, 4> SEHUnwindMap;
   SmallVector<ClrEHUnwindMapEntry, 4> ClrEHUnwindMap;
   int UnwindHelpFrameIdx = INT_MAX;
+  int PSPSymFrameIdx = INT_MAX;
 
   int getLastStateNumber() const { return CxxUnwindMap.size() - 1; }
 
index 7d257ee..da48a87 100644 (file)
@@ -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<unsigned>(Offset);
+}
+
+unsigned
+X86FrameLowering::getWinEHFuncletFrameSize(const MachineFunction &MF) const {
   // This is the size of the pushed CSRs.
   unsigned CSSize =
       MF.getInfo<X86MachineFunctionInfo>()->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 db32be1..753d155 100644 (file)
@@ -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;
 };
 
index 46b2b5e..fdb716e 100644 (file)
@@ -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;
index 923d966..079993f 100644 (file)
@@ -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