From 7c671b682f1a0c12f9509d26cd4e37e9d77a893b Mon Sep 17 00:00:00 2001
From: Christian Dietrich <christian.dietrich@informatik.uni-erlangen.de>
Date: Fri, 23 Feb 2018 14:42:59 +0100
Subject: [PATCH] x86: Supporting Stack-Switch on function call

modified X86FrameLowering.cpp as well as X86RegisterInfo.cpp.
added corresponding testcases: test/CodeGen/X86/stackswitch-symbol-*.ll

X86FrameLowering.cpp:
[emitPrologue]
Now each function is queried whether it has the function attribute
"stackswitch-sp-symbol" set or not. If it is set we assume that it's string
value is a global variable and then proceed to load the value of the global
variable into %esp. This happens exactly after moving the
old value of %esp to %ebp.
[emitEpilogue]
Also, because we now have a situation where the function arguments are on the
old stack but local variables are on the new stack, the %esp is being reset to
the last callee saved slot before popping them off.  (Same procedure as with
dynamic alloca being in use)

X86RegisterInfo.cpp:
As the %ebp is still pointing to the old stack, it can still be used as an
argument pointer. But the local variables are residing on the new stack, so
they have to be addressed by another register. This is done by using a
BasePointer (NOT %ebp, but some other register).

testcases:

stackswitch-symbol-basic.ll
Calls a function which changes the stack and returns the paramater it was given.

stackswitch-symbol-recursive.ll
After the stackswitch another recursive function is being called.
(The function which changes the stack must not be recursive by itself!)

stackswitch-symbol-dynamic.ll
The stack switching function uses it's paramter for dynamic memory allocation
on the stack.
---
 lib/Target/X86/X86FrameLowering.cpp           | 32 +++++++++++++----
 lib/Target/X86/X86RegisterInfo.cpp            |  3 ++
 test/CodeGen/X86/stackswitch-symbol-basic.ll  | 34 +++++++++++++++++++
 .../CodeGen/X86/stackswitch-symbol-dynamic.ll | 26 ++++++++++++++
 .../X86/stackswitch-symbol-recursion.ll       | 30 ++++++++++++++++
 5 files changed, 118 insertions(+), 7 deletions(-)
 create mode 100644 test/CodeGen/X86/stackswitch-symbol-basic.ll
 create mode 100644 test/CodeGen/X86/stackswitch-symbol-dynamic.ll
 create mode 100644 test/CodeGen/X86/stackswitch-symbol-recursion.ll

diff --git a/lib/Target/X86/X86FrameLowering.cpp b/lib/Target/X86/X86FrameLowering.cpp
index 545248ac100..667beffba0e 100644
--- a/lib/Target/X86/X86FrameLowering.cpp
+++ b/lib/Target/X86/X86FrameLowering.cpp
@@ -68,7 +68,7 @@ X86FrameLowering::canSimplifyCallFramePseudos(const MachineFunction &MF) const {
 // needsFrameIndexResolution - Do we need to perform FI resolution for
 // this function. Normally, this is required only when the function
 // has any stack objects. However, FI resolution actually has another job,
-// not apparent from the title - it resolves callframesetup/destroy 
+// not apparent from the title - it resolves callframesetup/destroy
 // that were not simplified earlier.
 // So, this is required for x86 functions that have push sequences even
 // when there are no stack objects.
@@ -78,6 +78,11 @@ X86FrameLowering::needsFrameIndexResolution(const MachineFunction &MF) const {
          MF.getInfo<X86MachineFunctionInfo>()->getHasPushSequences();
 }
 
+static
+bool hasStackSwitch(const MachineFunction &MF) {
+    return MF.getFunction().hasFnAttribute("stackswitch-sp-symbol");
+}
+
 /// hasFP - Return true if the specified function should have a dedicated frame
 /// pointer register.  This is true if the function has variable sized allocas
 /// or if frame pointer elimination is disabled.
@@ -85,6 +90,7 @@ bool X86FrameLowering::hasFP(const MachineFunction &MF) const {
   const MachineFrameInfo &MFI = MF.getFrameInfo();
   return (MF.getTarget().Options.DisableFramePointerElim(MF) ||
           TRI->needsStackRealignment(MF) ||
+          hasStackSwitch(MF) ||
           MFI.hasVarSizedObjects() ||
           MFI.isFrameAddressTaken() || MFI.hasOpaqueSPAdjustment() ||
           MF.getInfo<X86MachineFunctionInfo>()->getForceFramePointer() ||
@@ -603,7 +609,7 @@ void X86FrameLowering::emitStackProbeInline(MachineFunction &MF,
   int64_t RCXShadowSlot = 0;
   int64_t RDXShadowSlot = 0;
 
-  // If inlining in the prolog, save RCX and RDX.     
+  // If inlining in the prolog, save RCX and RDX.
   // Future optimization: don't save or restore if not live in.
   if (InProlog) {
     // Compute the offsets. We need to account for things already
@@ -1179,6 +1185,18 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
     }
   }
 
+  // Switch stack. TODO: more doc
+  if (hasStackSwitch(MF)) {
+      StringRef stack_symbol = Fn.getFnAttribute("stackswitch-sp-symbol").getValueAsString();
+      // X86AddressMode AM;
+      GlobalValue * GV = Fn.getParent()->getNamedValue(stack_symbol);
+      X86AddressMode AM;
+      AM.GV = GV;
+      addFullAddress(BuildMI(MBB, MBBI, DL,
+                             TII.get(Is64Bit ? X86::MOV64rm : X86::MOV32rm), StackPtr),
+                     AM);
+  }
+
   // Realign stack after we pushed callee-saved registers (so that we'll be
   // able to calculate their offsets from the frame pointer).
   // Don't do this for Win64, it needs to realign the stack after the prologue.
@@ -1621,9 +1639,9 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF,
   // slot before popping them off! Same applies for the case, when stack was
   // realigned. Don't do this if this was a funclet epilogue, since the funclets
   // will not do realignment or dynamic stack allocation.
-  if ((TRI->needsStackRealignment(MF) || MFI.hasVarSizedObjects()) &&
+  if ((TRI->needsStackRealignment(MF) || MFI.hasVarSizedObjects() || hasStackSwitch(MF)) &&
       !IsFunclet) {
-    if (TRI->needsStackRealignment(MF))
+    if (TRI->needsStackRealignment(MF) || hasStackSwitch(MF))
       MBBI = FirstCSPop;
     unsigned SEHFrameOffset = calculateSetFPREG(SEHStackAllocAmt);
     uint64_t LEAAmount =
@@ -2642,7 +2660,7 @@ bool X86FrameLowering::adjustStackWithPops(MachineBasicBlock &MBB,
     Regs[FoundRegs++] = Regs[0];
 
   for (int i = 0; i < NumPops; ++i)
-    BuildMI(MBB, MBBI, DL, 
+    BuildMI(MBB, MBBI, DL,
             TII.get(STI.is64Bit() ? X86::POP64r : X86::POP32r), Regs[i]);
 
   return true;
@@ -2923,7 +2941,7 @@ struct X86FrameSortingComparator {
     // in general. Something to keep in mind, though.
     if (DensityAScaled == DensityBScaled)
       return A.ObjectAlignment < B.ObjectAlignment;
-    
+
     return DensityAScaled < DensityBScaled;
   }
 };
@@ -2959,7 +2977,7 @@ void X86FrameLowering::orderFrameObjects(
     if (ObjectSize == 0)
       // Variable size. Just use 4.
       SortingObjects[Obj].ObjectSize = 4;
-    else      
+    else
       SortingObjects[Obj].ObjectSize = ObjectSize;
   }
 
diff --git a/lib/Target/X86/X86RegisterInfo.cpp b/lib/Target/X86/X86RegisterInfo.cpp
index f979cc51da4..690e51aa4fc 100644
--- a/lib/Target/X86/X86RegisterInfo.cpp
+++ b/lib/Target/X86/X86RegisterInfo.cpp
@@ -602,6 +602,9 @@ static bool CantUseSP(const MachineFrameInfo &MFI) {
 bool X86RegisterInfo::hasBasePointer(const MachineFunction &MF) const {
    const MachineFrameInfo &MFI = MF.getFrameInfo();
 
+   if (MF.getFunction().hasFnAttribute("stackswitch-sp-symbol"))
+     return true;
+
    if (!EnableBasePointer)
      return false;
 
diff --git a/test/CodeGen/X86/stackswitch-symbol-basic.ll b/test/CodeGen/X86/stackswitch-symbol-basic.ll
new file mode 100644
index 00000000000..dc618303cbd
--- /dev/null
+++ b/test/CodeGen/X86/stackswitch-symbol-basic.ll
@@ -0,0 +1,34 @@
+; RUN: llvm-as < %s | llc -mtriple=i386-pc-linux-gnu  \
+; RUN:              | FileCheck %s 
+
+
+@DOSEK_BasicTaskStack = external global i8*, align 8
+@.str = private unnamed_addr constant [3 x i8] c"%s\00", align 1
+@.str1 = private unnamed_addr constant [10 x i8] c"Success!\0A\00", align 1
+
+; Function Attrs: nounwind
+; Function Attrs: noinline nounwind uwtable
+define i32 @foo(i8*) #0 {
+; CHECK: movl %esp, %ebp
+; CHECK: movl DOSEK_BasicTaskStack, %esp
+; CHECK: movl %esp, %esi
+  %2 = alloca i8*, align 8
+  store i8* %0, i8** %2, align 8
+  %3 = load i8*, i8** %2, align 8
+  %4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i8* %3)
+  ret i32 %4
+; CHECK: leal -{{[0-9]+}}(%ebp), %esp
+; CHECK: popl %esi
+}
+
+declare i32 @printf(i8*, ...) 
+
+; Function Attrs: nounwind
+define i32 @main() {
+  %1 = alloca i32, align 4
+  store i32 0, i32* %1, align 4
+  %2 = call i32 @foo(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str1, i32 0, i32 0))
+  ret i32 %2
+}
+
+attributes #0 = { "stackswitch-sp-symbol"="DOSEK_BasicTaskStack" }
diff --git a/test/CodeGen/X86/stackswitch-symbol-dynamic.ll b/test/CodeGen/X86/stackswitch-symbol-dynamic.ll
new file mode 100644
index 00000000000..dcf4adb7463
--- /dev/null
+++ b/test/CodeGen/X86/stackswitch-symbol-dynamic.ll
@@ -0,0 +1,26 @@
+; RUN: llvm-as < %s | llc -mtriple=i386-pc-linux-gnu  \
+; RUN:              | FileCheck %s 
+
+@DOSEK_BasicTaskStack = external global i8*, align 8
+
+; Function Attrs: nounwind
+define void @mad(i32 %length) #0 {
+; CHECK: movl %esp, %ebp
+; CHECK: movl DOSEK_BasicTaskStack, %esp
+; CHECK: movl %esp, %esi
+  %m = alloca i32, i32 %length
+  ret void
+; CHECK: leal -{{[0-9]+}}(%ebp), %esp
+; CHECK: popl %esi
+}
+
+; Function Attrs: nounwind
+define i32 @main(i32 %argc, i8** %argv)  {
+; CHECK-NOT: DOSEK_BasicTaskStack
+  call void @mad(i32 32750)
+  ret i32 0
+}
+
+
+attributes #0 = { "stackswitch-sp-symbol"="DOSEK_BasicTaskStack"}
+
diff --git a/test/CodeGen/X86/stackswitch-symbol-recursion.ll b/test/CodeGen/X86/stackswitch-symbol-recursion.ll
new file mode 100644
index 00000000000..55f3229b5ca
--- /dev/null
+++ b/test/CodeGen/X86/stackswitch-symbol-recursion.ll
@@ -0,0 +1,30 @@
+; RUN: llvm-as < %s | llc -mtriple=i386-pc-linux-gnu  \
+; RUN:              | FileCheck %s 
+
+@DOSEK_BasicTaskStack = external global i8*, align 8
+
+define i32 @bar(i32 %start) {
+; CHECK-NOT: DOSEK_BasicTaskStack
+  %1 = call i32 @bar(i32 %start)
+  ret i32 %1
+
+}
+
+; Function Attrs: nounwind
+define i32 @foo(i32 %start) #0 {
+; CHECK: movl %esp, %ebp
+; CHECK: movl DOSEK_BasicTaskStack, %esp
+  %1 = call i32 @bar(i32 2)
+  ret i32 %1
+; CHECK: leal -{{[0-9]+}}(%ebp), %esp
+}
+
+; Function Attrs: nounwind
+define i32 @main(i32 %argc, i8** %argv)  {
+; CHECK-NOT: DOSEK_BasicTaskStack
+  %1 = call i32 @foo(i32 -10000)
+  ret i32 0
+}
+
+
+attributes #0 = { "stackswitch-sp-symbol"="DOSEK_BasicTaskStack" }
-- 
GitLab