[llvm] [X86][WinEH] Avoid stale RSP after Win64 dynalloca invokes (PR #191616)

via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 17 04:14:35 PDT 2026


https://github.com/taigacon updated https://github.com/llvm/llvm-project/pull/191616

>From c58297246f746685753a4b29ea5cdf1db0867187 Mon Sep 17 00:00:00 2001
From: taigacon <taigacon at gmail.com>
Date: Fri, 17 Apr 2026 11:23:04 +0800
Subject: [PATCH] [X86][WinEH] Avoid stale RSP after Win64 dynalloca invokes
 (#191616)

Thread the raw dynalloca size through DYNAMIC_STACKALLOC and lower
x86_64-windows-msvc dynamic allocas to a dedicated WIN_ALLOCA pseudo.

Track a stable outgoing call-frame size for Win64/MSVC dynalloca
functions, keep the reserved-call-frame model, and make each WIN_ALLOCA
subtract only the allocation size plus alignment slack while still
returning rsp + CallFrameSize.

Update the affected Win64 codegen tests to match the resulting MSVC
code shape.
---
 llvm/include/llvm/CodeGen/ISDOpcodes.h        |   9 +-
 .../SelectionDAG/SelectionDAGBuilder.cpp      |   4 +-
 .../Target/AArch64/AArch64ISelLowering.cpp    |   2 +-
 llvm/lib/Target/X86/X86DynAllocaExpander.cpp  | 242 ++++++++++++++----
 llvm/lib/Target/X86/X86FrameLowering.cpp      |  26 +-
 llvm/lib/Target/X86/X86ISelLowering.cpp       |   8 +
 llvm/lib/Target/X86/X86InstrCompiler.td       |   8 +
 llvm/lib/Target/X86/X86InstrFragments.td      |   7 +
 llvm/lib/Target/X86/X86MachineFunctionInfo.h  |  13 +
 .../test/CodeGen/X86/alloca-align-rounding.ll |  10 +
 .../CodeGen/X86/catchpad-dynamic-alloca.ll    |  37 ++-
 llvm/test/CodeGen/X86/localescape.ll          |  19 +-
 llvm/test/CodeGen/X86/stack-protector-msvc.ll |  77 +++---
 .../CodeGen/X86/win64_alloca_dynalloca.ll     | 111 ++++++--
 llvm/test/CodeGen/X86/win64_frame.ll          | 108 +++++---
 .../DebugInfo/X86/instr-ref-dyn-alloca.ll     |  13 +-
 16 files changed, 523 insertions(+), 171 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index 8a8a9ee71ca02..ce2b176bdc7b8 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -1168,10 +1168,11 @@ enum NodeType {
   /// DYNAMIC_STACKALLOC - Allocate some number of bytes on the stack aligned
   /// to a specified boundary.  This node always has two return values: a new
   /// stack pointer value and a chain. The first operand is the token chain,
-  /// the second is the number of bytes to allocate, and the third is the
-  /// alignment boundary.  The size is guaranteed to be a multiple of the
-  /// stack alignment, and the alignment is guaranteed to be bigger than the
-  /// stack alignment (if required) or 0 to get standard stack alignment.
+  /// the second is the number of bytes to allocate, the third is the
+  /// alignment boundary, and the fourth is the unrounded allocation size.
+  /// The size is guaranteed to be a multiple of the stack alignment, and the
+  /// alignment is guaranteed to be bigger than the stack alignment (if
+  /// required) or 0 to get standard stack alignment.
   DYNAMIC_STACKALLOC,
 
   /// Control flow instructions.  These all have token chains.
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 1f3b099c9c577..27306a54ed14d 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -4636,6 +4636,7 @@ void SelectionDAGBuilder::visitAlloca(const AllocaInst &I) {
   AllocSize = DAG.getNode(
       ISD::MUL, dl, IntPtr, AllocSize,
       DAG.getZExtOrTrunc(DAG.getTypeSize(dl, MVT::i64, TySize), dl, IntPtr));
+  SDValue RawAllocSize = AllocSize;
 
   // Handle alignment.  If the requested alignment is less than or equal to
   // the stack alignment, ignore it.  If the size is greater than or equal to
@@ -4658,7 +4659,8 @@ void SelectionDAGBuilder::visitAlloca(const AllocaInst &I) {
 
   SDValue Ops[] = {
       getRoot(), AllocSize,
-      DAG.getConstant(Alignment ? Alignment->value() : 0, dl, IntPtr)};
+      DAG.getConstant(Alignment ? Alignment->value() : 0, dl, IntPtr),
+      RawAllocSize};
   SDVTList VTs = DAG.getVTList(AllocSize.getValueType(), MVT::Other);
   SDValue DSA = DAG.getNode(ISD::DYNAMIC_STACKALLOC, dl, VTs, Ops);
   setValue(&I, DSA);
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index ece24767bdbb9..72895171f26dc 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -9079,7 +9079,7 @@ SDValue AArch64TargetLowering::LowerFormalArguments(
     if (Size) {
       SDValue Buffer = DAG.getNode(
           ISD::DYNAMIC_STACKALLOC, DL, DAG.getVTList(MVT::i64, MVT::Other),
-          {Chain, Size, DAG.getConstant(1, DL, MVT::i64)});
+          {Chain, Size, DAG.getConstant(1, DL, MVT::i64), Size});
       Chain = Buffer.getValue(1);
 
       Register BufferPtr =
diff --git a/llvm/lib/Target/X86/X86DynAllocaExpander.cpp b/llvm/lib/Target/X86/X86DynAllocaExpander.cpp
index bb4301c11c270..6693271d0f834 100644
--- a/llvm/lib/Target/X86/X86DynAllocaExpander.cpp
+++ b/llvm/lib/Target/X86/X86DynAllocaExpander.cpp
@@ -15,6 +15,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "X86.h"
+#include "X86InstrBuilder.h"
 #include "X86InstrInfo.h"
 #include "X86MachineFunctionInfo.h"
 #include "X86Subtarget.h"
@@ -50,6 +51,10 @@ class X86DynAllocaExpander {
   /// Get the appropriate lowering based on current offset and amount.
   Lowering getLowering(int64_t CurrentOffset, int64_t AllocaAmount);
 
+  Register materializeWinAllocaAmount(MachineInstr *MI,
+                                      MachineBasicBlock::iterator I,
+                                      const DebugLoc &DL);
+
   /// Lower a DynAlloca instruction.
   void lower(MachineInstr* MI, Lowering L);
 
@@ -88,13 +93,17 @@ FunctionPass *llvm::createX86DynAllocaExpanderLegacyPass() {
 }
 
 /// Return the allocation amount for a DynAlloca instruction, or -1 if unknown.
-static int64_t getDynAllocaAmount(MachineInstr *MI, MachineRegisterInfo *MRI) {
-  assert(MI->getOpcode() == X86::DYN_ALLOCA_32 ||
-         MI->getOpcode() == X86::DYN_ALLOCA_64);
-  assert(MI->getOperand(0).isReg());
+static bool isDynAllocaOpcode(unsigned Opc) {
+  return Opc == X86::DYN_ALLOCA_32 || Opc == X86::DYN_ALLOCA_64 ||
+         Opc == X86::WIN_ALLOCA_64;
+}
+
+static unsigned getDynAllocaAmountOperandIndex(const MachineInstr *MI) {
+  return MI->getOpcode() == X86::WIN_ALLOCA_64 ? 1 : 0;
+}
 
-  Register AmountReg = MI->getOperand(0).getReg();
-  MachineInstr *Def = MRI->getUniqueVRegDef(AmountReg);
+static int64_t getConstantRegValue(Register Reg, MachineRegisterInfo *MRI) {
+  MachineInstr *Def = MRI->getUniqueVRegDef(Reg);
 
   if (!Def ||
       (Def->getOpcode() != X86::MOV32ri && Def->getOpcode() != X86::MOV64ri) ||
@@ -104,6 +113,36 @@ static int64_t getDynAllocaAmount(MachineInstr *MI, MachineRegisterInfo *MRI) {
   return Def->getOperand(1).getImm();
 }
 
+static int64_t getWinAllocaAmount(MachineInstr *MI, MachineRegisterInfo *MRI,
+                                  unsigned StackAlign) {
+  assert(MI->getOpcode() == X86::WIN_ALLOCA_64);
+  assert(MI->getOperand(1).isReg());
+
+  int64_t RawAmount = getConstantRegValue(MI->getOperand(1).getReg(), MRI);
+  if (RawAmount < 0)
+    return -1;
+
+  uint64_t Amount = static_cast<uint64_t>(RawAmount);
+  unsigned Alignment = MI->getOperand(2).getImm();
+  if (Alignment > StackAlign)
+    Amount += Alignment - 1;
+  Amount = alignTo(Amount, StackAlign);
+  if (Amount > static_cast<uint64_t>(INT64_MAX))
+    return -1;
+  return static_cast<int64_t>(Amount);
+}
+
+static int64_t getDynAllocaAmount(MachineInstr *MI, MachineRegisterInfo *MRI,
+                                  unsigned StackAlign = 0) {
+  assert(isDynAllocaOpcode(MI->getOpcode()));
+  if (MI->getOpcode() == X86::WIN_ALLOCA_64)
+    return getWinAllocaAmount(MI, MRI, StackAlign);
+
+  unsigned AmountOperandIndex = getDynAllocaAmountOperandIndex(MI);
+  assert(MI->getOperand(AmountOperandIndex).isReg());
+  return getConstantRegValue(MI->getOperand(AmountOperandIndex).getReg(), MRI);
+}
+
 X86DynAllocaExpander::Lowering
 X86DynAllocaExpander::getLowering(int64_t CurrentOffset,
                                   int64_t AllocaAmount) {
@@ -154,6 +193,8 @@ void X86DynAllocaExpander::computeLowerings(MachineFunction &MF,
 
   // Compute the reverse post-order.
   ReversePostOrderTraversal<MachineFunction*> RPO(&MF);
+  bool HasStableWin64MSVCCallFrame =
+      MF.getInfo<X86MachineFunctionInfo>()->hasWin64MSVCDynAllocaCallFrame();
 
   for (MachineBasicBlock *MBB : RPO) {
     int64_t Offset = -1;
@@ -162,10 +203,10 @@ void X86DynAllocaExpander::computeLowerings(MachineFunction &MF,
     if (Offset == -1) Offset = INT32_MAX;
 
     for (MachineInstr &MI : *MBB) {
-      if (MI.getOpcode() == X86::DYN_ALLOCA_32 ||
-          MI.getOpcode() == X86::DYN_ALLOCA_64) {
+      if (isDynAllocaOpcode(MI.getOpcode())) {
         // A DynAlloca moves StackPtr, and potentially touches it.
-        int64_t Amount = getDynAllocaAmount(&MI, MRI);
+        int64_t Amount = getDynAllocaAmount(
+            &MI, MRI, STI->getFrameLowering()->getStackAlign().value());
         Lowering L = getLowering(Offset, Amount);
         Lowerings[&MI] = L;
         switch (L) {
@@ -184,10 +225,14 @@ void X86DynAllocaExpander::computeLowerings(MachineFunction &MF,
         Offset = 0;
       } else if (MI.getOpcode() == X86::ADJCALLSTACKUP32 ||
                  MI.getOpcode() == X86::ADJCALLSTACKUP64) {
-        Offset -= MI.getOperand(0).getImm();
+        if (!HasStableWin64MSVCCallFrame ||
+            MI.getOpcode() != X86::ADJCALLSTACKUP64)
+          Offset -= MI.getOperand(0).getImm();
       } else if (MI.getOpcode() == X86::ADJCALLSTACKDOWN32 ||
                  MI.getOpcode() == X86::ADJCALLSTACKDOWN64) {
-        Offset += MI.getOperand(0).getImm();
+        if (!HasStableWin64MSVCCallFrame ||
+            MI.getOpcode() != X86::ADJCALLSTACKDOWN64)
+          Offset += MI.getOperand(0).getImm();
       } else if (MI.modifiesRegister(StackPtr, TRI)) {
         // Any other modification of SP means we've lost track of it.
         Offset = INT32_MAX;
@@ -204,13 +249,47 @@ static unsigned getSubOpcode(bool Is64Bit) {
   return X86::SUB32ri;
 }
 
+Register X86DynAllocaExpander::materializeWinAllocaAmount(
+    MachineInstr *MI, MachineBasicBlock::iterator I, const DebugLoc &DL) {
+  assert(MI->getOpcode() == X86::WIN_ALLOCA_64);
+
+  MachineBasicBlock *MBB = MI->getParent();
+  unsigned StackAlign = STI->getFrameLowering()->getStackAlign().value();
+  unsigned Alignment = MI->getOperand(2).getImm();
+  Register RawAmountReg = MI->getOperand(1).getReg();
+
+  uint64_t AlignmentSlack = Alignment > StackAlign ? Alignment - 1 : 0;
+  uint64_t RoundUpBias = StackAlign - 1;
+  uint64_t Addend = AlignmentSlack + RoundUpBias;
+
+  Register AddendReg = MRI->createVirtualRegister(&X86::GR64RegClass);
+  BuildMI(*MBB, I, DL, TII->get(X86::MOV64ri), AddendReg).addImm(Addend);
+
+  Register UnalignedAmountReg = MRI->createVirtualRegister(&X86::GR64RegClass);
+  BuildMI(*MBB, I, DL, TII->get(X86::ADD64rr), UnalignedAmountReg)
+      .addReg(RawAmountReg)
+      .addReg(AddendReg);
+
+  Register AmountReg = MRI->createVirtualRegister(&X86::GR64RegClass);
+  BuildMI(*MBB, I, DL, TII->get(X86::AND64ri32), AmountReg)
+      .addReg(UnalignedAmountReg)
+      .addImm(-int64_t(StackAlign));
+  return AmountReg;
+}
+
 void X86DynAllocaExpander::lower(MachineInstr *MI, Lowering L) {
   const DebugLoc &DL = MI->getDebugLoc();
   MachineBasicBlock *MBB = MI->getParent();
+  unsigned Win64MSVCDynAllocaCallFrameSize =
+      MBB->getParent()
+          ->getInfo<X86MachineFunctionInfo>()
+          ->getWin64MSVCDynAllocaCallFrameSize();
   MachineBasicBlock::iterator I = *MI;
 
-  int64_t Amount = getDynAllocaAmount(MI, MRI);
-  if (Amount == 0) {
+  bool IsWinAlloca = MI->getOpcode() == X86::WIN_ALLOCA_64;
+  int64_t Amount = getDynAllocaAmount(
+      MI, MRI, STI->getFrameLowering()->getStackAlign().value());
+  if (Amount == 0 && !IsWinAlloca) {
     MI->eraseFromParent();
     return;
   }
@@ -218,65 +297,102 @@ void X86DynAllocaExpander::lower(MachineInstr *MI, Lowering L) {
   // These two variables differ on x32, which is a 64-bit target with a
   // 32-bit alloca.
   bool Is64Bit = STI->is64Bit();
-  bool Is64BitAlloca = MI->getOpcode() == X86::DYN_ALLOCA_64;
+  bool Is64BitAlloca = MI->getOpcode() == X86::DYN_ALLOCA_64 ||
+                       MI->getOpcode() == X86::WIN_ALLOCA_64;
   assert(SlotSize == 4 || SlotSize == 8);
 
   std::optional<MachineFunction::DebugInstrOperandPair> InstrNum;
-  if (unsigned Num = MI->peekDebugInstrNum()) {
+  if (unsigned Num = MI->peekDebugInstrNum(); !IsWinAlloca && Num) {
     // Operand 2 of DYN_ALLOCAs contains the stack def.
     InstrNum = {Num, 2};
   }
 
-  switch (L) {
-  case TouchAndSub: {
-    assert(Amount >= SlotSize);
+  unsigned AmountOperandIndex = getDynAllocaAmountOperandIndex(MI);
+  Register AmountReg = MI->getOperand(AmountOperandIndex).getReg();
 
-    // Use a push to touch the top of the stack.
-    unsigned RegA = Is64Bit ? X86::RAX : X86::EAX;
-    BuildMI(*MBB, I, DL, TII->get(Is64Bit ? X86::PUSH64r : X86::PUSH32r))
-        .addReg(RegA, RegState::Undef);
-    Amount -= SlotSize;
-    if (!Amount)
-      break;
+  if (Amount != 0) {
+    switch (L) {
+    case TouchAndSub: {
+      assert(Amount >= SlotSize);
 
-    // Fall through to make any remaining adjustment.
-    [[fallthrough]];
-  }
-  case Sub:
-    assert(Amount > 0);
-    if (Amount == SlotSize) {
-      // Use push to save size.
+      // Use a push to touch the top of the stack.
       unsigned RegA = Is64Bit ? X86::RAX : X86::EAX;
       BuildMI(*MBB, I, DL, TII->get(Is64Bit ? X86::PUSH64r : X86::PUSH32r))
           .addReg(RegA, RegState::Undef);
-    } else {
-      // Sub.
-      BuildMI(*MBB, I, DL, TII->get(getSubOpcode(Is64BitAlloca)), StackPtr)
-          .addReg(StackPtr)
-          .addImm(Amount);
+      Amount -= SlotSize;
+      if (!Amount)
+        break;
+
+      // Fall through to make any remaining adjustment.
+      [[fallthrough]];
+    }
+    case Sub:
+      assert(Amount > 0);
+      if (Amount == SlotSize) {
+        // Use push to save size.
+        unsigned RegA = Is64Bit ? X86::RAX : X86::EAX;
+        BuildMI(*MBB, I, DL, TII->get(Is64Bit ? X86::PUSH64r : X86::PUSH32r))
+            .addReg(RegA, RegState::Undef);
+      } else {
+        // Sub.
+        BuildMI(*MBB, I, DL, TII->get(getSubOpcode(Is64BitAlloca)), StackPtr)
+            .addReg(StackPtr)
+            .addImm(Amount);
+      }
+      break;
+    case Probe:
+      Register ProbeAmountReg = AmountReg;
+      if (IsWinAlloca) {
+        if (Amount >= 0) {
+          ProbeAmountReg = MRI->createVirtualRegister(&X86::GR64RegClass);
+          BuildMI(*MBB, I, DL, TII->get(X86::MOV64ri), ProbeAmountReg)
+              .addImm(Amount);
+        } else {
+          ProbeAmountReg = materializeWinAllocaAmount(MI, I, DL);
+        }
+      }
+      if (!NoStackArgProbe) {
+        // The probe lowering expects the amount in RAX/EAX.
+        unsigned RegA = Is64BitAlloca ? X86::RAX : X86::EAX;
+        BuildMI(*MBB, MI, DL, TII->get(TargetOpcode::COPY), RegA)
+            .addReg(ProbeAmountReg);
+
+        // Do the probe.
+        STI->getFrameLowering()->emitStackProbe(*MBB->getParent(), *MBB, MI, DL,
+                                                /*InProlog=*/false, InstrNum);
+      } else {
+        // Sub
+        BuildMI(*MBB, I, DL,
+                TII->get(Is64BitAlloca ? X86::SUB64rr : X86::SUB32rr), StackPtr)
+            .addReg(StackPtr)
+            .addReg(ProbeAmountReg);
+      }
+      break;
     }
-    break;
-  case Probe:
-    if (!NoStackArgProbe) {
-      // The probe lowering expects the amount in RAX/EAX.
-      unsigned RegA = Is64BitAlloca ? X86::RAX : X86::EAX;
-      BuildMI(*MBB, MI, DL, TII->get(TargetOpcode::COPY), RegA)
-          .addReg(MI->getOperand(0).getReg());
-
-      // Do the probe.
-      STI->getFrameLowering()->emitStackProbe(*MBB->getParent(), *MBB, MI, DL,
-                                              /*InProlog=*/false, InstrNum);
+  }
+
+  if (IsWinAlloca) {
+    Register DstReg = MI->getOperand(0).getReg();
+    unsigned Alignment = MI->getOperand(2).getImm();
+    unsigned StackAlign = STI->getFrameLowering()->getStackAlign().value();
+
+    if (Alignment > StackAlign) {
+      Register TmpReg = MRI->createVirtualRegister(&X86::GR64RegClass);
+      addRegOffset(BuildMI(*MBB, I, DL, TII->get(X86::LEA64r), TmpReg),
+                   StackPtr, false,
+                   Win64MSVCDynAllocaCallFrameSize + Alignment - 1);
+      BuildMI(*MBB, I, DL, TII->get(X86::AND64ri32), DstReg)
+          .addReg(TmpReg)
+          .addImm(-int64_t(Alignment));
+    } else if (Win64MSVCDynAllocaCallFrameSize != 0) {
+      addRegOffset(BuildMI(*MBB, I, DL, TII->get(X86::LEA64r), DstReg),
+                   StackPtr, false, Win64MSVCDynAllocaCallFrameSize);
     } else {
-      // Sub
-      BuildMI(*MBB, I, DL,
-              TII->get(Is64BitAlloca ? X86::SUB64rr : X86::SUB32rr), StackPtr)
-          .addReg(StackPtr)
-          .addReg(MI->getOperand(0).getReg());
+      BuildMI(*MBB, I, DL, TII->get(TargetOpcode::COPY), DstReg)
+          .addReg(StackPtr);
     }
-    break;
   }
 
-  Register AmountReg = MI->getOperand(0).getReg();
   MI->eraseFromParent();
 
   // Delete the definition of AmountReg.
@@ -297,6 +413,22 @@ bool X86DynAllocaExpander::run(MachineFunction &MF) {
   SlotSize = TRI->getSlotSize();
   StackProbeSize = STI->getTargetLowering()->getStackProbeSize(MF);
   NoStackArgProbe = MF.getFunction().hasFnAttribute("no-stack-arg-probe");
+  auto *X86FI = MF.getInfo<X86MachineFunctionInfo>();
+  auto &MFI = MF.getFrameInfo();
+
+  unsigned Win64MSVCDynAllocaCallFrameSize = 0;
+  if (STI->isTargetWin64() && STI->isTargetWindowsMSVC()) {
+    MFI.computeMaxCallFrameSize(MF);
+    Align StackAlign = STI->getFrameLowering()->getStackAlign();
+    Win64MSVCDynAllocaCallFrameSize =
+        static_cast<unsigned>(alignTo(MFI.getMaxCallFrameSize(), StackAlign));
+    assert(Win64MSVCDynAllocaCallFrameSize == 0 ||
+           !X86FI->getHasPushSequences());
+    assert(Win64MSVCDynAllocaCallFrameSize == 0 ||
+           !X86FI->hasPreallocatedCall());
+  }
+  X86FI->setWin64MSVCDynAllocaCallFrameSize(Win64MSVCDynAllocaCallFrameSize);
+
   if (NoStackArgProbe)
     StackProbeSize = INT64_MAX;
 
diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp
index 438ba03554dfd..a44306907296a 100644
--- a/llvm/lib/Target/X86/X86FrameLowering.cpp
+++ b/llvm/lib/Target/X86/X86FrameLowering.cpp
@@ -46,6 +46,26 @@ STATISTIC(NumFunctionUsingPush2Pop2, "Number of functions using push2/pop2");
 
 using namespace llvm;
 
+static bool hasConstantSP(const MachineFunction &MF) {
+  const auto *X86FI = MF.getInfo<X86MachineFunctionInfo>();
+  return !MF.getFrameInfo().hasVarSizedObjects() &&
+         !X86FI->getHasPushSequences() && !X86FI->hasPreallocatedCall();
+}
+
+static bool hasWin64MSVCDynAllocaCallFrame(const MachineFunction &MF) {
+  const auto &STI = MF.getSubtarget<X86Subtarget>();
+  if (!STI.isTargetWin64() || !STI.isTargetWindowsMSVC())
+    return false;
+
+  const auto *X86FI = MF.getInfo<X86MachineFunctionInfo>();
+  if (!X86FI->hasWin64MSVCDynAllocaCallFrame())
+    return false;
+
+  assert(!X86FI->getHasPushSequences());
+  assert(!X86FI->hasPreallocatedCall());
+  return true;
+}
+
 X86FrameLowering::X86FrameLowering(const X86Subtarget &STI,
                                    MaybeAlign StackAlignOverride)
     : TargetFrameLowering(StackGrowsDown, StackAlignOverride.valueOrOne(),
@@ -62,9 +82,7 @@ X86FrameLowering::X86FrameLowering(const X86Subtarget &STI,
 }
 
 bool X86FrameLowering::hasReservedCallFrame(const MachineFunction &MF) const {
-  return !MF.getFrameInfo().hasVarSizedObjects() &&
-         !MF.getInfo<X86MachineFunctionInfo>()->getHasPushSequences() &&
-         !MF.getInfo<X86MachineFunctionInfo>()->hasPreallocatedCall();
+  return hasConstantSP(MF) || hasWin64MSVCDynAllocaCallFrame(MF);
 }
 
 /// canSimplifyCallFramePseudos - If there is a reserved call frame, the
@@ -2815,7 +2833,7 @@ X86FrameLowering::getFrameIndexReferencePreferSP(const MachineFunction &MF,
   // If !hasReservedCallFrame the function might have SP adjustement in the
   // body.  So, even though the offset is statically known, it depends on where
   // we are in the function.
-  if (!IgnoreSPUpdates && !hasReservedCallFrame(MF))
+  if (!IgnoreSPUpdates && !hasConstantSP(MF))
     return getFrameIndexReference(MF, FI, FrameReg);
 
   // We don't handle tail calls, and shouldn't be seeing them either.
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index e647286301d6b..cc9a138a5b120 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -26548,6 +26548,7 @@ X86TargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op,
   SDValue Chain = Op.getOperand(0);
   SDValue Size  = Op.getOperand(1);
   MaybeAlign Alignment(Op.getConstantOperandVal(2));
+  SDValue RawSize = Op.getOperand(3);
   EVT VT = Node->getValueType(0);
 
   // Chain the dynamic stack allocation so that it doesn't modify the stack
@@ -26595,6 +26596,13 @@ X86TargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op,
     Result =
         DAG.getNode(X86ISD::SEG_ALLOCA, dl, {SPTy, MVT::Other}, {Chain, Size});
     Chain = Result.getValue(1);
+  } else if (Subtarget.isTargetWin64() && Subtarget.isTargetWindowsMSVC()) {
+    SDVTList NodeTys = DAG.getVTList(SPTy, MVT::Other);
+    Result = DAG.getNode(X86ISD::WIN_ALLOCA, dl, NodeTys, Chain, RawSize,
+                         DAG.getTargetConstant(
+                             Alignment ? Alignment->value() : 0, dl, MVT::i32));
+    Chain = Result.getValue(1);
+    MF.getInfo<X86MachineFunctionInfo>()->setHasDynAlloca(true);
   } else {
     SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
     Chain = DAG.getNode(X86ISD::DYN_ALLOCA, dl, NodeTys, Chain, Size);
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index ebbfa48d2660c..568fb939bd861 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -158,6 +158,14 @@ def DYN_ALLOCA_64 : I<0, Pseudo, (outs), (ins GR64:$size),
                      "# dynamic stack allocation",
                      [(X86DynAlloca GR64:$size)]>,
                      Requires<[In64BitMode]>;
+
+let Defs = [RAX, RSP, EFLAGS], Uses = [RSP] in
+def WIN_ALLOCA_64 : I<0, Pseudo, (outs GR64:$dst),
+                     (ins GR64:$raw_size, i32imm:$align),
+                     "# dynamic stack allocation with a stable call frame",
+                     [(set GR64:$dst,
+                           (X86WinAlloca GR64:$raw_size, timm:$align))]>,
+                     Requires<[In64BitMode]>;
 } // SchedRW
 
 // These instructions XOR the frame pointer into a GPR. They are used in some
diff --git a/llvm/lib/Target/X86/X86InstrFragments.td b/llvm/lib/Target/X86/X86InstrFragments.td
index bae1211e51330..02d20863be0ea 100644
--- a/llvm/lib/Target/X86/X86InstrFragments.td
+++ b/llvm/lib/Target/X86/X86InstrFragments.td
@@ -114,6 +114,10 @@ def SDT_X86TLSCALL : SDTypeProfile<0, 1, [SDTCisInt<0>]>;
 
 def SDT_X86DYN_ALLOCA : SDTypeProfile<0, 1, [SDTCisVT<0, iPTR>]>;
 
+def SDT_X86WIN_ALLOCA : SDTypeProfile<1, 2, [SDTCisVT<0, iPTR>,
+                                             SDTCisVT<1, iPTR>,
+                                             SDTCisVT<2, i32>]>;
+
 def SDT_X86SEG_ALLOCA : SDTypeProfile<1, 1, [SDTCisVT<0, iPTR>, SDTCisVT<1, iPTR>]>;
 
 def SDT_X86PROBED_ALLOCA : SDTypeProfile<1, 1, [SDTCisVT<0, iPTR>, SDTCisVT<1, iPTR>]>;
@@ -427,6 +431,9 @@ def X86mul_imm : SDNode<"X86ISD::MUL_IMM", SDTIntBinOp>;
 def X86DynAlloca : SDNode<"X86ISD::DYN_ALLOCA", SDT_X86DYN_ALLOCA,
                           [SDNPHasChain, SDNPOutGlue]>;
 
+def X86WinAlloca : SDNode<"X86ISD::WIN_ALLOCA", SDT_X86WIN_ALLOCA,
+                          [SDNPHasChain]>;
+
 // For allocating variable amounts of stack space when using
 // segmented stacks. Check if the current stacklet has enough space, and
 // falls back to heap allocation if not.
diff --git a/llvm/lib/Target/X86/X86MachineFunctionInfo.h b/llvm/lib/Target/X86/X86MachineFunctionInfo.h
index 1bda505ed39f1..94686bf811c7a 100644
--- a/llvm/lib/Target/X86/X86MachineFunctionInfo.h
+++ b/llvm/lib/Target/X86/X86MachineFunctionInfo.h
@@ -116,6 +116,9 @@ class X86MachineFunctionInfo : public MachineFunctionInfo {
   unsigned ArgumentStackSize = 0;
   /// NumLocalDynamics - Number of local-dynamic TLS accesses.
   unsigned NumLocalDynamics = 0;
+  /// On x86_64-windows-msvc dynalloca functions, stores the rounded stable
+  /// outgoing call-frame size inferred from ADJCALLSTACKDOWN64/UP64 pseudos.
+  unsigned Win64MSVCDynAllocaCallFrameSize = 0;
   /// HasPushSequences - Keeps track of whether this function uses sequences
   /// of pushes to pass function parameters.
   bool HasPushSequences = false;
@@ -281,6 +284,16 @@ class X86MachineFunctionInfo : public MachineFunctionInfo {
   bool hasPreallocatedCall() const { return HasPreallocatedCall; }
   void setHasPreallocatedCall(bool v) { HasPreallocatedCall = v; }
 
+  bool hasWin64MSVCDynAllocaCallFrame() const {
+    return Win64MSVCDynAllocaCallFrameSize != 0;
+  }
+  unsigned getWin64MSVCDynAllocaCallFrameSize() const {
+    return Win64MSVCDynAllocaCallFrameSize;
+  }
+  void setWin64MSVCDynAllocaCallFrameSize(unsigned Size) {
+    Win64MSVCDynAllocaCallFrameSize = Size;
+  }
+
   bool hasSwiftAsyncContext() const { return HasSwiftAsyncContext; }
   void setHasSwiftAsyncContext(bool v) { HasSwiftAsyncContext = v; }
 
diff --git a/llvm/test/CodeGen/X86/alloca-align-rounding.ll b/llvm/test/CodeGen/X86/alloca-align-rounding.ll
index 38866ceaca32c..49efa47227de7 100644
--- a/llvm/test/CodeGen/X86/alloca-align-rounding.ll
+++ b/llvm/test/CodeGen/X86/alloca-align-rounding.ll
@@ -1,5 +1,6 @@
 ; RUN: llc < %s -mtriple=x86_64-pc-linux -enable-misched=false | FileCheck %s
 ; RUN: llc < %s -mtriple=x86_64-pc-linux-gnux32 -enable-misched=false | FileCheck %s -check-prefix=X32ABI
+; RUN: llc < %s -mtriple=x86_64-pc-win32 -enable-misched=false | FileCheck %s -check-prefix=WIN64
 
 declare void @bar(ptr %n)
 
@@ -20,6 +21,15 @@ define void @foo2(i64 %h) {
 ; CHECK-LABEL: foo2
 ; CHECK: andq $-32, %rsp
 ; CHECK: andq $-32, %rax
+; WIN64-LABEL: foo2
+; WIN64: andq $-32, %rsp
+; WIN64: movabsq $46, %rax
+; WIN64: addq %rcx, %rax
+; WIN64: andq $-16, %rax
+; WIN64: callq __chkstk
+; WIN64: subq %rax, %rsp
+; WIN64: leaq 63(%rsp), %rcx
+; WIN64: andq $-32, %rcx
 ; X32ABI-LABEL: foo2
 ; X32ABI: andl $-32, %esp
 ; X32ABI: andl $-32, %eax
diff --git a/llvm/test/CodeGen/X86/catchpad-dynamic-alloca.ll b/llvm/test/CodeGen/X86/catchpad-dynamic-alloca.ll
index 6b63162717e7f..46323555a2f32 100644
--- a/llvm/test/CodeGen/X86/catchpad-dynamic-alloca.ll
+++ b/llvm/test/CodeGen/X86/catchpad-dynamic-alloca.ll
@@ -31,10 +31,27 @@ catch.switch:
   %cs = catchswitch within none [label %catch.pad] unwind to caller
 }
 
+; CHECK-LABEL: test1:
+; CHECK:      movabsq $15, %rax
+; CHECK-NEXT: addq    %rdx, %rax
+; CHECK-NEXT: andq    $-16, %rax
+; CHECK-NEXT: callq   __chkstk
+; CHECK-NEXT: subq    %rax, %rsp
+; CHECK-NEXT: leaq    32(%rsp), %rax
+; CHECK-NEXT: movb    $0, -9(%rbp)
+; CHECK-NEXT: movb    $0, (%rax)
+; CHECK:      callq   rt_init
+; CHECK-NOT:  subq
+; CHECK-NOT:  addq
+; CHECK:      callq   *%rsi
+; CHECK-LABEL: "?catch$3@?0?test1 at 4HA":
+; CHECK:      leaq    48(%rdx), %rbp
 ; CHECK-LABEL: $handlerMap$0$test1:
 ; CHECK:      .long   0
 ; CHECK-NEXT: .long   0
-; CHECK-NEXT: .long   16
+; CHECK-NEXT: .long   48
+; CHECK-NEXT: .long   "?catch$3@?0?test1 at 4HA"@IMGREL
+; CHECK-NEXT: .long   72
 
 define void @test2(ptr %fp, i64 %n) personality ptr @__CxxFrameHandler3 {
 entry:
@@ -59,7 +76,23 @@ catch.switch:
   %cs = catchswitch within none [label %catch.pad] unwind to caller
 }
 
+; CHECK-LABEL: test2:
+; CHECK:      movabsq $15, %rax
+; CHECK-NEXT: addq    %rdx, %rax
+; CHECK-NEXT: andq    $-16, %rax
+; CHECK-NEXT: callq   __chkstk
+; CHECK-NEXT: subq    %rax, %rsp
+; CHECK-NEXT: leaq    32(%rsp), %rax
+; CHECK-NEXT: movb    $0, (%rax)
+; CHECK:      callq   rt_init
+; CHECK-NOT:  subq
+; CHECK-NOT:  addq
+; CHECK:      callq   *%rsi
+; CHECK-LABEL: "?catch$3@?0?test2 at 4HA":
+; CHECK:      leaq    64(%rdx), %rbp
 ; CHECK-LABEL: $handlerMap$0$test2:
 ; CHECK:      .long   0
 ; CHECK-NEXT: .long   0
-; CHECK-NEXT: .long   16
+; CHECK-NEXT: .long   48
+; CHECK-NEXT: .long   "?catch$3@?0?test2 at 4HA"@IMGREL
+; CHECK-NEXT: .long   72
diff --git a/llvm/test/CodeGen/X86/localescape.ll b/llvm/test/CodeGen/X86/localescape.ll
index 57369be489af3..951b2fe09da34 100644
--- a/llvm/test/CodeGen/X86/localescape.ll
+++ b/llvm/test/CodeGen/X86/localescape.ll
@@ -72,16 +72,25 @@ define void @alloc_func(i32 %n) {
 
 ; X64-LABEL: alloc_func:
 ; X64: pushq   %rbp
-; X64: subq    $16, %rsp
-; X64: .seh_stackalloc 16
-; X64: leaq    16(%rsp), %rbp
-; X64: .seh_setframe %rbp, 16
+; X64: subq    $48, %rsp
+; X64: .seh_stackalloc 48
+; X64: leaq    48(%rsp), %rbp
+; X64: .seh_setframe %rbp, 48
+; X64: .seh_endprologue
 ; X64: .Lalloc_func$frame_escape_0 = -4
 ; X64: .Lalloc_func$frame_escape_1 = -12
 ; X64: movl $42, -4(%rbp)
 ; X64: movl $13, -12(%rbp)
-; X64: movq 	%rbp, %rcx
+; X64: movl %ecx, %ecx
+; X64: movabsq $15, %rax
+; X64: addq %rcx, %rax
+; X64: andq $-16, %rax
+; X64: callq __chkstk
+; X64: subq %rax, %rsp
+; X64: leaq 32(%rsp), %rax
+; X64: movq %rbp, %rcx
 ; X64: callq print_framealloc_from_fp
+; X64: movq %rbp, %rsp
 ; X64: retq
 
 ; X86-LABEL: alloc_func:
diff --git a/llvm/test/CodeGen/X86/stack-protector-msvc.ll b/llvm/test/CodeGen/X86/stack-protector-msvc.ll
index 3109733e0b0b7..c8cfa2085077a 100644
--- a/llvm/test/CodeGen/X86/stack-protector-msvc.ll
+++ b/llvm/test/CodeGen/X86/stack-protector-msvc.ll
@@ -176,20 +176,20 @@ define void @test_vla(i32 %n) nounwind ssp {
 ; MSVC-X64-LABEL: test_vla:
 ; MSVC-X64:       # %bb.0:
 ; MSVC-X64-NEXT:    pushq %rbp
-; MSVC-X64-NEXT:    subq $16, %rsp
-; MSVC-X64-NEXT:    leaq {{[0-9]+}}(%rsp), %rbp
+; MSVC-X64-NEXT:    subq $48, %rsp
+; MSVC-X64-NEXT:    leaq 48(%rsp), %rbp
 ; MSVC-X64-NEXT:    movq __security_cookie(%rip), %rax
 ; MSVC-X64-NEXT:    xorq %rbp, %rax
 ; MSVC-X64-NEXT:    movq %rax, -8(%rbp)
-; MSVC-X64-NEXT:    movl %ecx, %eax
-; MSVC-X64-NEXT:    leaq 15(,%rax,4), %rax
+; MSVC-X64-NEXT:    movl %ecx, %ecx
+; MSVC-X64-NEXT:    shlq $2, %rcx
+; MSVC-X64-NEXT:    movabsq $15, %rax
+; MSVC-X64-NEXT:    addq %rcx, %rax
 ; MSVC-X64-NEXT:    andq $-16, %rax
 ; MSVC-X64-NEXT:    callq __chkstk
 ; MSVC-X64-NEXT:    subq %rax, %rsp
-; MSVC-X64-NEXT:    movq %rsp, %rcx
-; MSVC-X64-NEXT:    subq $32, %rsp
+; MSVC-X64-NEXT:    leaq 32(%rsp), %rcx
 ; MSVC-X64-NEXT:    callq escape
-; MSVC-X64-NEXT:    addq $32, %rsp
 ; MSVC-X64-NEXT:    movq -8(%rbp), %rax
 ; MSVC-X64-NEXT:    xorq %rbp, %rax
 ; MSVC-X64-NEXT:    movq __security_cookie(%rip), %rcx
@@ -202,7 +202,6 @@ define void @test_vla(i32 %n) nounwind ssp {
 ; MSVC-X64-NEXT:  .LBB1_2:
 ; MSVC-X64-NEXT:    movq -8(%rbp), %rcx
 ; MSVC-X64-NEXT:    xorq %rbp, %rcx
-; MSVC-X64-NEXT:    subq $32, %rsp
 ; MSVC-X64-NEXT:    callq __security_check_cookie
 ;
 ; MSVC-X86-O0-LABEL: test_vla:
@@ -239,21 +238,21 @@ define void @test_vla(i32 %n) nounwind ssp {
 ; MSVC-X64-O0-LABEL: test_vla:
 ; MSVC-X64-O0:       # %bb.0:
 ; MSVC-X64-O0-NEXT:    pushq %rbp
-; MSVC-X64-O0-NEXT:    subq $16, %rsp
-; MSVC-X64-O0-NEXT:    leaq {{[0-9]+}}(%rsp), %rbp
+; MSVC-X64-O0-NEXT:    subq $48, %rsp
+; MSVC-X64-O0-NEXT:    leaq 48(%rsp), %rbp
 ; MSVC-X64-O0-NEXT:    movq __security_cookie(%rip), %rax
 ; MSVC-X64-O0-NEXT:    xorq %rbp, %rax
 ; MSVC-X64-O0-NEXT:    movq %rax, -8(%rbp)
 ; MSVC-X64-O0-NEXT:    movl %ecx, %eax
 ; MSVC-X64-O0-NEXT:    # kill: def $rax killed $eax
-; MSVC-X64-O0-NEXT:    leaq 15(,%rax,4), %rax
+; MSVC-X64-O0-NEXT:    shlq $2, %rax
+; MSVC-X64-O0-NEXT:    movabsq $15, %rcx
+; MSVC-X64-O0-NEXT:    addq %rcx, %rax
 ; MSVC-X64-O0-NEXT:    andq $-16, %rax
 ; MSVC-X64-O0-NEXT:    callq __chkstk
 ; MSVC-X64-O0-NEXT:    subq %rax, %rsp
-; MSVC-X64-O0-NEXT:    movq %rsp, %rcx
-; MSVC-X64-O0-NEXT:    subq $32, %rsp
+; MSVC-X64-O0-NEXT:    leaq 32(%rsp), %rcx
 ; MSVC-X64-O0-NEXT:    callq escape
-; MSVC-X64-O0-NEXT:    addq $32, %rsp
 ; MSVC-X64-O0-NEXT:    movq -8(%rbp), %rcx
 ; MSVC-X64-O0-NEXT:    xorq %rbp, %rcx
 ; MSVC-X64-O0-NEXT:    movq __security_cookie(%rip), %rax
@@ -263,7 +262,6 @@ define void @test_vla(i32 %n) nounwind ssp {
 ; MSVC-X64-O0-NEXT:  .LBB1_2:
 ; MSVC-X64-O0-NEXT:    movq -8(%rbp), %rcx
 ; MSVC-X64-O0-NEXT:    xorq %rbp, %rcx
-; MSVC-X64-O0-NEXT:    subq $32, %rsp
 ; MSVC-X64-O0-NEXT:    callq __security_check_cookie
 ; MSVC-X64-O0-NEXT:  .LBB1_1:
 ; MSVC-X64-O0-NEXT:    movq %rbp, %rsp
@@ -323,26 +321,26 @@ define void @test_vla_realign(i32 %n) nounwind ssp {
 ; MSVC-X64-NEXT:    pushq %rbp
 ; MSVC-X64-NEXT:    pushq %rsi
 ; MSVC-X64-NEXT:    pushq %rbx
-; MSVC-X64-NEXT:    subq $32, %rsp
-; MSVC-X64-NEXT:    leaq {{[0-9]+}}(%rsp), %rbp
+; MSVC-X64-NEXT:    subq $64, %rsp
+; MSVC-X64-NEXT:    leaq 64(%rsp), %rbp
 ; MSVC-X64-NEXT:    andq $-32, %rsp
 ; MSVC-X64-NEXT:    movq %rsp, %rbx
 ; MSVC-X64-NEXT:    movq __security_cookie(%rip), %rax
 ; MSVC-X64-NEXT:    xorq %rbp, %rax
-; MSVC-X64-NEXT:    movq %rax, 24(%rbx)
-; MSVC-X64-NEXT:    movl %ecx, %eax
-; MSVC-X64-NEXT:    leaq 15(,%rax,4), %rax
+; MSVC-X64-NEXT:    movq %rax, 56(%rbx)
+; MSVC-X64-NEXT:    movl %ecx, %ecx
+; MSVC-X64-NEXT:    shlq $2, %rcx
+; MSVC-X64-NEXT:    movabsq $15, %rax
+; MSVC-X64-NEXT:    addq %rcx, %rax
 ; MSVC-X64-NEXT:    andq $-16, %rax
 ; MSVC-X64-NEXT:    callq __chkstk
 ; MSVC-X64-NEXT:    subq %rax, %rsp
-; MSVC-X64-NEXT:    movq %rsp, %rsi
-; MSVC-X64-NEXT:    subq $32, %rsp
-; MSVC-X64-NEXT:    movq %rbx, %rcx
+; MSVC-X64-NEXT:    leaq 32(%rsp), %rsi
+; MSVC-X64-NEXT:    leaq 32(%rbx), %rcx
 ; MSVC-X64-NEXT:    callq escape
 ; MSVC-X64-NEXT:    movq %rsi, %rcx
 ; MSVC-X64-NEXT:    callq escape
-; MSVC-X64-NEXT:    addq $32, %rsp
-; MSVC-X64-NEXT:    movq 24(%rbx), %rax
+; MSVC-X64-NEXT:    movq 56(%rbx), %rax
 ; MSVC-X64-NEXT:    xorq %rbp, %rax
 ; MSVC-X64-NEXT:    movq __security_cookie(%rip), %rcx
 ; MSVC-X64-NEXT:    cmpq %rax, %rcx
@@ -354,9 +352,8 @@ define void @test_vla_realign(i32 %n) nounwind ssp {
 ; MSVC-X64-NEXT:    popq %rbp
 ; MSVC-X64-NEXT:    retq
 ; MSVC-X64-NEXT:  .LBB2_2:
-; MSVC-X64-NEXT:    movq 24(%rbx), %rcx
+; MSVC-X64-NEXT:    movq 56(%rbx), %rcx
 ; MSVC-X64-NEXT:    xorq %rbp, %rcx
-; MSVC-X64-NEXT:    subq $32, %rsp
 ; MSVC-X64-NEXT:    callq __security_check_cookie
 ;
 ; MSVC-X86-O0-LABEL: test_vla_realign:
@@ -403,37 +400,36 @@ define void @test_vla_realign(i32 %n) nounwind ssp {
 ; MSVC-X64-O0:       # %bb.0:
 ; MSVC-X64-O0-NEXT:    pushq %rbp
 ; MSVC-X64-O0-NEXT:    pushq %rbx
-; MSVC-X64-O0-NEXT:    subq $72, %rsp
-; MSVC-X64-O0-NEXT:    leaq {{[0-9]+}}(%rsp), %rbp
+; MSVC-X64-O0-NEXT:    subq $104, %rsp
+; MSVC-X64-O0-NEXT:    leaq 96(%rsp), %rbp
 ; MSVC-X64-O0-NEXT:    andq $-32, %rsp
 ; MSVC-X64-O0-NEXT:    movq %rsp, %rbx
 ; MSVC-X64-O0-NEXT:    movq __security_cookie(%rip), %rax
 ; MSVC-X64-O0-NEXT:    xorq %rbp, %rax
-; MSVC-X64-O0-NEXT:    movq %rax, 64(%rbx)
+; MSVC-X64-O0-NEXT:    movq %rax, 96(%rbx)
 ; MSVC-X64-O0-NEXT:    movl %ecx, %eax
 ; MSVC-X64-O0-NEXT:    # kill: def $rax killed $eax
-; MSVC-X64-O0-NEXT:    leaq 15(,%rax,4), %rax
+; MSVC-X64-O0-NEXT:    shlq $2, %rax
+; MSVC-X64-O0-NEXT:    movabsq $15, %rcx
+; MSVC-X64-O0-NEXT:    addq %rcx, %rax
 ; MSVC-X64-O0-NEXT:    andq $-16, %rax
 ; MSVC-X64-O0-NEXT:    callq __chkstk
 ; MSVC-X64-O0-NEXT:    subq %rax, %rsp
-; MSVC-X64-O0-NEXT:    movq %rsp, %rax
-; MSVC-X64-O0-NEXT:    movq %rax, 24(%rbx) # 8-byte Spill
-; MSVC-X64-O0-NEXT:    leaq 32(%rbx), %rcx
-; MSVC-X64-O0-NEXT:    subq $32, %rsp
+; MSVC-X64-O0-NEXT:    leaq 32(%rsp), %rax
+; MSVC-X64-O0-NEXT:    movq %rax, 56(%rbx) # 8-byte Spill
+; MSVC-X64-O0-NEXT:    leaq 64(%rbx), %rcx
 ; MSVC-X64-O0-NEXT:    callq escape
-; MSVC-X64-O0-NEXT:    movq 24(%rbx), %rcx # 8-byte Reload
+; MSVC-X64-O0-NEXT:    movq 56(%rbx), %rcx # 8-byte Reload
 ; MSVC-X64-O0-NEXT:    callq escape
-; MSVC-X64-O0-NEXT:    addq $32, %rsp
-; MSVC-X64-O0-NEXT:    movq 64(%rbx), %rcx
+; MSVC-X64-O0-NEXT:    movq 96(%rbx), %rcx
 ; MSVC-X64-O0-NEXT:    xorq %rbp, %rcx
 ; MSVC-X64-O0-NEXT:    movq __security_cookie(%rip), %rax
 ; MSVC-X64-O0-NEXT:    subq %rcx, %rax
 ; MSVC-X64-O0-NEXT:    jne .LBB2_2
 ; MSVC-X64-O0-NEXT:    jmp .LBB2_1
 ; MSVC-X64-O0-NEXT:  .LBB2_2:
-; MSVC-X64-O0-NEXT:    movq 64(%rbx), %rcx
+; MSVC-X64-O0-NEXT:    movq 96(%rbx), %rcx
 ; MSVC-X64-O0-NEXT:    xorq %rbp, %rcx
-; MSVC-X64-O0-NEXT:    subq $32, %rsp
 ; MSVC-X64-O0-NEXT:    callq __security_check_cookie
 ; MSVC-X64-O0-NEXT:  .LBB2_1:
 ; MSVC-X64-O0-NEXT:    leaq 8(%rbp), %rsp
@@ -450,4 +446,3 @@ define void @test_vla_realign(i32 %n) nounwind ssp {
 declare ptr @strcpy(ptr, ptr) nounwind
 
 declare i32 @printf(ptr, ...) nounwind
-
diff --git a/llvm/test/CodeGen/X86/win64_alloca_dynalloca.ll b/llvm/test/CodeGen/X86/win64_alloca_dynalloca.ll
index 241188b8cc3d5..36b58a6d2573a 100644
--- a/llvm/test/CodeGen/X86/win64_alloca_dynalloca.ll
+++ b/llvm/test/CodeGen/X86/win64_alloca_dynalloca.ll
@@ -20,13 +20,13 @@ entry:
 ; M64: leaq 128(%rsp), %rbp
 
 ; __chkstk does not adjust %rsp.
-; W64:       $4096, %eax
+; W64:       $4144, %eax
 ; W64: callq __chkstk
 ; W64: subq  %rax, %rsp
 ; W64: leaq 128(%rsp), %rbp
 
 ; Use %r11 for the large model.
-; L64:       $4096, %eax
+; L64:       $4144, %eax
 ; L64: movabsq $__chkstk, %r11
 ; L64: callq *%r11
 ; L64: subq  %rax, %rsp
@@ -43,18 +43,20 @@ entry:
 ; M64: subq  %rax, %rsp
 ; M64: movq  %rsp, %rax
 
-; W64: leaq  15(%{{.*}}), %rax
+; W64: movabsq $15, %rax
+; W64: addq  %rcx, %rax
 ; W64: andq  $-16, %rax
 ; W64: callq __chkstk
 ; W64: subq  %rax, %rsp
-; W64: movq  %rsp, %rax
+; W64: leaq  48(%rsp), %rax
 
-; L64: leaq  15(%{{.*}}), %rax
+; L64: movabsq $15, %rax
+; L64: addq  %rcx, %rax
 ; L64: andq  $-16, %rax
 ; L64: movabsq $__chkstk, %r11
 ; L64: callq *%r11
 ; L64: subq  %rax, %rsp
-; L64: movq  %rsp, %rax
+; L64: leaq  48(%rsp), %rax
 
 ; EFI: leaq  15(%{{.*}}), [[R1:%r.*]]
 ; EFI: andq  $-16, [[R1]]
@@ -69,9 +71,8 @@ entry:
 ; M64: leaq  -128(%rbp), %r9
 ; M64: callq bar
 
-; W64: subq  $48, %rsp
 ; W64: movq  %rax, 32(%rsp)
-; W64: leaq  -128(%rbp), %r9
+; W64: leaq  -80(%rbp), %r9
 ; W64: callq bar
 
 ; EFI: subq  $48, %rsp
@@ -83,11 +84,11 @@ entry:
 
 ; M64: leaq    3968(%rbp), %rsp
 
-; W64: leaq    3968(%rbp), %rsp
+; W64: leaq    4016(%rbp), %rsp
 
 }
 
-define i64 @aligned(i64 %n, i64 %x) nounwind {
+define i64 @aligned(i64 %n, i64 %x, ptr %dummy) nounwind {
 ; M64-LABEL: aligned:
 ; W64-LABEL: aligned:
 ; EFI-LABEL: aligned:
@@ -103,13 +104,13 @@ entry:
 ; M64: andq  $-128, [[R2]]
 ; M64: movq  [[R2]], %rsp
 
-; W64: leaq  15(%{{.*}}), %rax
+; W64: movabsq $142, %rax
+; W64: addq  %rcx, %rax
 ; W64: andq  $-16, %rax
 ; W64: callq __chkstk
 ; W64: subq  %rax, %rsp
-; W64: movq  %rsp, [[R2:%r.*]]
+; W64: leaq  175(%rsp), [[R2:%r.*]]
 ; W64: andq  $-128, [[R2]]
-; W64: movq  [[R2]], %rsp
 
 ; EFI: leaq  15(%{{.*}}), [[R1:%r.*]]
 ; EFI: andq  $-16, [[R1]]
@@ -118,13 +119,12 @@ entry:
 ; EFI: andq  $-128, [[R64]]
 ; EFI: movq  [[R64]], %rsp
 
-  %r = call i64 @bar(i64 %n, i64 %x, i64 %n, ptr undef, ptr %buf1) nounwind
+  %r = call i64 @bar(i64 %n, i64 %x, i64 %n, ptr %dummy, ptr %buf1) nounwind
 
 ; M64: subq  $48, %rsp
 ; M64: movq  [[R2]], 32(%rsp)
 ; M64: callq bar
 
-; W64: subq  $48, %rsp
 ; W64: movq  [[R2]], 32(%rsp)
 ; W64: callq bar
 
@@ -135,4 +135,85 @@ entry:
   ret i64 %r
 }
 
+define void @two_allocas(i64 %a, i64 %b) nounwind {
+; W64-LABEL: two_allocas:
+; W64:       movabsq $15, %rax
+; W64-NEXT:  addq %rcx, %rax
+; W64-NEXT:  andq $-16, %rax
+; W64-NEXT:  callq __chkstk
+; W64-NEXT:  subq %rax, %rsp
+; W64-NEXT:  leaq 32(%rsp), [[P:%r[a-z0-9]+]]
+; W64:       movq [[P]], %rcx
+; W64-NEXT:  callq use
+; W64:       movabsq $15, %rax
+; W64-NEXT:  addq %rsi, %rax
+; W64-NEXT:  andq $-16, %rax
+; W64-NEXT:  callq __chkstk
+; W64-NEXT:  subq %rax, %rsp
+; W64-NEXT:  leaq 32(%rsp), [[Q:%r[a-z0-9]+]]
+; W64:       movq [[P]], %rcx
+; W64-NEXT:  callq use
+; W64-NEXT:  movq [[Q]], %rcx
+; W64-NEXT:  callq use
+entry:
+  %p = alloca i8, i64 %a, align 1
+  call void @use(ptr %p)
+  %q = alloca i8, i64 %b, align 1
+  call void @use(ptr %p)
+  call void @use(ptr %q)
+  ret void
+}
+
+define void @two_allocas_aligned(i64 %a, i64 %b) nounwind {
+; W64-LABEL: two_allocas_aligned:
+; W64:       movabsq $142, %rax
+; W64-NEXT:  addq %rcx, %rax
+; W64-NEXT:  andq $-16, %rax
+; W64-NEXT:  callq __chkstk
+; W64-NEXT:  subq %rax, %rsp
+; W64-NEXT:  leaq 159(%rsp), [[P:%r[a-z0-9]+]]
+; W64-NEXT:  andq $-128, [[P]]
+; W64:       movabsq $15, %rax
+; W64-NEXT:  addq %rdx, %rax
+; W64-NEXT:  andq $-16, %rax
+; W64-NEXT:  callq __chkstk
+; W64-NEXT:  subq %rax, %rsp
+; W64-NEXT:  leaq 32(%rsp), [[Q:%r[a-z0-9]+]]
+; W64:       movq [[P]], %rcx
+; W64-NEXT:  callq use
+; W64-NEXT:  movq [[Q]], %rcx
+; W64-NEXT:  callq use
+; W64-NEXT:  movq [[P]], %rcx
+; W64-NEXT:  callq use
+entry:
+  %p = alloca i8, i64 %a, align 128
+  %q = alloca i8, i64 %b, align 1
+  call void @use(ptr %p)
+  call void @use(ptr %q)
+  call void @use(ptr %p)
+  ret void
+}
+
+define i64 @aligned_stack_args(i64 %n, i64 %x, ptr %dummy) nounwind {
+; W64-LABEL: aligned_stack_args:
+; W64:       movabsq $78, %rax
+; W64-NEXT:  addq  %rcx, %rax
+; W64-NEXT:  andq  $-16, %rax
+; W64-NEXT:  callq __chkstk
+; W64-NEXT:  subq  %rax, %rsp
+; W64-NEXT:  leaq  111(%rsp), [[P:%r[a-z0-9]+]]
+; W64-NEXT:  andq  $-64, [[P]]
+; W64:       movq  [[P]], 40(%rsp)
+; W64-NEXT:  movq  [[P]], 32(%rsp)
+; W64-NOT:   subq
+; W64-NOT:   addq
+; W64:       callq baz
+entry:
+  %p = alloca i8, i64 %n, align 64
+  %r = call i64 @baz(i64 %n, i64 %x, i64 %n, ptr %dummy, ptr %p, ptr %p) nounwind
+  ret i64 %r
+}
+
 declare i64 @bar(i64, i64, i64, ptr nocapture, ptr nocapture) nounwind
+declare i64 @baz(i64, i64, i64, ptr nocapture, ptr nocapture, ptr nocapture) nounwind
+declare void @use(ptr) nounwind
diff --git a/llvm/test/CodeGen/X86/win64_frame.ll b/llvm/test/CodeGen/X86/win64_frame.ll
index c4b36c5e263c8..5f8450a69ae77 100644
--- a/llvm/test/CodeGen/X86/win64_frame.ll
+++ b/llvm/test/CodeGen/X86/win64_frame.ll
@@ -1,8 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc < %s -mtriple=x86_64-pc-win32              | FileCheck %s
-; RUN: llc < %s -mtriple=x86_64-pc-win32 -mattr=+sahf | FileCheck %s
-; RUN: llc < %s -mtriple=x86_64-uefi              | FileCheck %s
-; RUN: llc < %s -mtriple=x86_64-uefi -mattr=+sahf | FileCheck %s
+; RUN: llc < %s -mtriple=x86_64-pc-win32              | FileCheck %s --check-prefixes=CHECK,WIN32
+; RUN: llc < %s -mtriple=x86_64-pc-win32 -mattr=+sahf | FileCheck %s --check-prefixes=CHECK,WIN32
+; RUN: llc < %s -mtriple=x86_64-uefi                  | FileCheck %s --check-prefixes=CHECK,EFI
+; RUN: llc < %s -mtriple=x86_64-uefi -mattr=+sahf     | FileCheck %s --check-prefixes=CHECK,EFI
 
 define i32 @f1(i32 %p1, i32 %p2, i32 %p3, i32 %p4, i32 %p5) "frame-pointer"="all" {
 ; CHECK-LABEL: f1:
@@ -161,39 +161,73 @@ define i32 @f7(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) "frame-pointer"="all" {
 
 define i32 @f8(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) "frame-pointer"="all" {
 ; CHECK-LABEL: f8:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    pushq %rbp
-; CHECK-NEXT:    .seh_pushreg %rbp
-; CHECK-NEXT:    pushq %rsi
-; CHECK-NEXT:    .seh_pushreg %rsi
-; CHECK-NEXT:    pushq %rbx
-; CHECK-NEXT:    .seh_pushreg %rbx
-; CHECK-NEXT:    subq $352, %rsp # imm = 0x160
-; CHECK-NEXT:    .seh_stackalloc 352
-; CHECK-NEXT:    leaq {{[0-9]+}}(%rsp), %rbp
-; CHECK-NEXT:    .seh_setframe %rbp, 128
-; CHECK-NEXT:    .seh_endprologue
-; CHECK-NEXT:    andq $-64, %rsp
-; CHECK-NEXT:    movq %rsp, %rbx
-; CHECK-NEXT:    movl 288(%rbp), %esi
-; CHECK-NEXT:    movl %ecx, %eax
-; CHECK-NEXT:    leaq 15(,%rax,4), %rax
-; CHECK-NEXT:    andq $-16, %rax
-; CHECK-NEXT:    callq __chkstk
-; CHECK-NEXT:    subq %rax, %rsp
-; CHECK-NEXT:    subq $32, %rsp
-; CHECK-NEXT:    movq %rbx, %rcx
-; CHECK-NEXT:    callq external
-; CHECK-NEXT:    addq $32, %rsp
-; CHECK-NEXT:    movl %esi, %eax
-; CHECK-NEXT:    .seh_startepilogue
-; CHECK-NEXT:    leaq 224(%rbp), %rsp
-; CHECK-NEXT:    popq %rbx
-; CHECK-NEXT:    popq %rsi
-; CHECK-NEXT:    popq %rbp
-; CHECK-NEXT:    .seh_endepilogue
-; CHECK-NEXT:    retq
-; CHECK-NEXT:    .seh_endproc
+; WIN32:       # %bb.0:
+; WIN32-NEXT:    pushq %rbp
+; WIN32-NEXT:    .seh_pushreg %rbp
+; WIN32-NEXT:    pushq %rsi
+; WIN32-NEXT:    .seh_pushreg %rsi
+; WIN32-NEXT:    pushq %rbx
+; WIN32-NEXT:    .seh_pushreg %rbx
+; WIN32-NEXT:    subq $416, %rsp # imm = 0x1A0
+; WIN32-NEXT:    .seh_stackalloc 416
+; WIN32-NEXT:    leaq {{[0-9]+}}(%rsp), %rbp
+; WIN32-NEXT:    .seh_setframe %rbp, 128
+; WIN32-NEXT:    .seh_endprologue
+; WIN32-NEXT:    andq $-64, %rsp
+; WIN32-NEXT:    movq %rsp, %rbx
+; WIN32-NEXT:    movl 352(%rbp), %esi
+; WIN32-NEXT:    movl %ecx, %ecx
+; WIN32-NEXT:    shlq $2, %rcx
+; WIN32-NEXT:    movabsq $15, %rax
+; WIN32-NEXT:    addq %rcx, %rax
+; WIN32-NEXT:    andq $-16, %rax
+; WIN32-NEXT:    callq __chkstk
+; WIN32-NEXT:    subq %rax, %rsp
+; WIN32-NEXT:    leaq 32(%rsp), %rax
+; WIN32-NEXT:    leaq 64(%rbx), %rcx
+; WIN32-NEXT:    callq external
+; WIN32-NEXT:    movl %esi, %eax
+; WIN32-NEXT:    .seh_startepilogue
+; WIN32-NEXT:    leaq 288(%rbp), %rsp
+; WIN32-NEXT:    popq %rbx
+; WIN32-NEXT:    popq %rsi
+; WIN32-NEXT:    popq %rbp
+; WIN32-NEXT:    .seh_endepilogue
+; WIN32-NEXT:    retq
+; WIN32-NEXT:    .seh_endproc
+; EFI:       # %bb.0:
+; EFI-NEXT:    pushq %rbp
+; EFI-NEXT:    .seh_pushreg %rbp
+; EFI-NEXT:    pushq %rsi
+; EFI-NEXT:    .seh_pushreg %rsi
+; EFI-NEXT:    pushq %rbx
+; EFI-NEXT:    .seh_pushreg %rbx
+; EFI-NEXT:    subq $352, %rsp # imm = 0x160
+; EFI-NEXT:    .seh_stackalloc 352
+; EFI-NEXT:    leaq {{[0-9]+}}(%rsp), %rbp
+; EFI-NEXT:    .seh_setframe %rbp, 128
+; EFI-NEXT:    .seh_endprologue
+; EFI-NEXT:    andq $-64, %rsp
+; EFI-NEXT:    movq %rsp, %rbx
+; EFI-NEXT:    movl 288(%rbp), %esi
+; EFI-NEXT:    movl %ecx, %eax
+; EFI-NEXT:    leaq 15(,%rax,4), %rax
+; EFI-NEXT:    andq $-16, %rax
+; EFI-NEXT:    callq __chkstk
+; EFI-NEXT:    subq %rax, %rsp
+; EFI-NEXT:    subq $32, %rsp
+; EFI-NEXT:    movq %rbx, %rcx
+; EFI-NEXT:    callq external
+; EFI-NEXT:    addq $32, %rsp
+; EFI-NEXT:    movl %esi, %eax
+; EFI-NEXT:    .seh_startepilogue
+; EFI-NEXT:    leaq 224(%rbp), %rsp
+; EFI-NEXT:    popq %rbx
+; EFI-NEXT:    popq %rsi
+; EFI-NEXT:    popq %rbp
+; EFI-NEXT:    .seh_endepilogue
+; EFI-NEXT:    retq
+; EFI-NEXT:    .seh_endproc
   %alloca = alloca [300 x i8], align 64
   alloca i32, i32 %a
   call void @external(ptr %alloca)
diff --git a/llvm/test/DebugInfo/X86/instr-ref-dyn-alloca.ll b/llvm/test/DebugInfo/X86/instr-ref-dyn-alloca.ll
index 5af743caee433..2234f3349630e 100644
--- a/llvm/test/DebugInfo/X86/instr-ref-dyn-alloca.ll
+++ b/llvm/test/DebugInfo/X86/instr-ref-dyn-alloca.ll
@@ -4,13 +4,14 @@
 
 ;; Copy of instr-ref-dyn-alloca-win32.ll, targetting 64 bit Windows. Here,
 ;; _chkstk doesn't return with a modified stack pointer, instead we have to
-;; edit it with our own subtract operation. Check that it's labelled and the
-;; substitution is correct. This also covers the code paths for non-Windows on
-;; x86.
+;; edit it with our own subtract operation. Check that the lowered stack
+;; pointer copy is what the DBG_INSTR_REF points at. This also covers the code
+;; paths for non-Windows on x86.
 
-; DYN_LOWERED:      debugValueSubstitutions:
-; DYN_LOWERED-NEXT: - { srcinst: 1, srcop: 2, dstinst: 2, dstop: 0, subreg: 0 }
-; DYN_LOWERED:      SUB64rr $rsp, killed $rax, {{.*}} debug-instr-number 2,
+; DYN_LOWERED:      debugValueSubstitutions: []
+; DYN_LOWERED:      $rsp = SUB64rr $rsp, killed $rax, {{.*}}
+; DYN_LOWERED-NEXT: %{{[0-9]+}}:gr64 = COPY $rsp, debug-location !41
+; DYN_LOWERED-NEXT: DBG_INSTR_REF !42, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_deref, DW_OP_deref), dbg-instr-ref(1, 0), debug-location !46
 
 source_filename = "test/DebugInfo/COFF/types-array-advanced.ll"
 target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"



More information about the llvm-commits mailing list