[llvm] [X86][WinEH] Restore parent RSP before Win64 catchret continuation (PR #191616)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 16 02:14:40 PDT 2026
https://github.com/taigacon updated https://github.com/llvm/llvm-project/pull/191616
>From 511fba1f71ce5cea0ea4c4464f1509466a902733 Mon Sep 17 00:00:00 2001
From: taigacon <taigacon at gmail.com>
Date: Tue, 14 Apr 2026 15:46:23 +0800
Subject: [PATCH 1/3] [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 | 245 ++++++++++++++----
llvm/lib/Target/X86/X86FrameLowering.cpp | 18 +-
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 | 86 +++++-
llvm/test/CodeGen/X86/win64_frame.ll | 108 +++++---
15 files changed, 488 insertions(+), 163 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..bc786a20783da 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;
+}
- Register AmountReg = MI->getOperand(0).getReg();
- MachineInstr *Def = MRI->getUniqueVRegDef(AmountReg);
+static unsigned getDynAllocaAmountOperandIndex(const MachineInstr *MI) {
+ return MI->getOpcode() == X86::WIN_ALLOCA_64 ? 1 : 0;
+}
+
+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 Addend = StackAlign - 1;
+ if (Alignment > StackAlign)
+ Addend += Alignment - 1;
+
+ 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]];
}
- 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);
+ 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;
+ }
+ }
+
+ 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,25 @@ 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>();
+
+ unsigned Win64MSVCDynAllocaCallFrameSize = 0;
+ if (STI->isTargetWin64() && STI->isTargetWindowsMSVC() &&
+ !X86FI->getHasPushSequences() && !X86FI->hasPreallocatedCall()) {
+ Align StackAlign = STI->getFrameLowering()->getStackAlign();
+ for (const MachineBasicBlock &MBB : MF) {
+ for (const MachineInstr &MI : MBB) {
+ if (MI.getOpcode() == X86::ADJCALLSTACKDOWN64 ||
+ MI.getOpcode() == X86::ADJCALLSTACKUP64) {
+ Win64MSVCDynAllocaCallFrameSize = std::max<unsigned>(
+ Win64MSVCDynAllocaCallFrameSize,
+ alignTo(MI.getOperand(0).getImm(), StackAlign));
+ }
+ }
+ }
+ }
+ 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..5deaeb119ab75 100644
--- a/llvm/lib/Target/X86/X86FrameLowering.cpp
+++ b/llvm/lib/Target/X86/X86FrameLowering.cpp
@@ -46,6 +46,14 @@ STATISTIC(NumFunctionUsingPush2Pop2, "Number of functions using push2/pop2");
using namespace llvm;
+static bool hasWin64MSVCDynAllocaCallFrame(const MachineFunction &MF) {
+ const auto &STI = MF.getSubtarget<X86Subtarget>();
+ const auto *X86FI = MF.getInfo<X86MachineFunctionInfo>();
+ return STI.isTargetWin64() && STI.isTargetWindowsMSVC() &&
+ X86FI->hasWin64MSVCDynAllocaCallFrame() &&
+ !X86FI->getHasPushSequences() && !X86FI->hasPreallocatedCall();
+}
+
X86FrameLowering::X86FrameLowering(const X86Subtarget &STI,
MaybeAlign StackAlignOverride)
: TargetFrameLowering(StackGrowsDown, StackAlignOverride.valueOrOne(),
@@ -62,9 +70,10 @@ 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 ((!MF.getFrameInfo().hasVarSizedObjects() &&
+ !MF.getInfo<X86MachineFunctionInfo>()->getHasPushSequences() &&
+ !MF.getInfo<X86MachineFunctionInfo>()->hasPreallocatedCall()) ||
+ hasWin64MSVCDynAllocaCallFrame(MF));
}
/// canSimplifyCallFramePseudos - If there is a reserved call frame, the
@@ -2815,7 +2824,8 @@ 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 &&
+ (!hasReservedCallFrame(MF) || MFI.hasVarSizedObjects()))
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..edbc307c05166 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..a82a7e4474abf 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,7 +84,7 @@ entry:
; M64: leaq 3968(%rbp), %rsp
-; W64: leaq 3968(%rbp), %rsp
+; W64: leaq 4016(%rbp), %rsp
}
@@ -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]]
@@ -124,7 +125,6 @@ entry:
; M64: movq [[R2]], 32(%rsp)
; M64: callq bar
-; W64: subq $48, %rsp
; W64: movq [[R2]], 32(%rsp)
; W64: callq bar
@@ -135,4 +135,64 @@ 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
+}
+
declare i64 @bar(i64, i64, i64, 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)
>From da85e15f451454fc0699cac37870076b5ed6d78d Mon Sep 17 00:00:00 2001
From: "yuxuan.ding" <yuxuan.ding at mihoyo.com>
Date: Wed, 15 Apr 2026 11:54:05 +0800
Subject: [PATCH 2/3] fixup! [X86][WinEH] Avoid stale RSP after Win64 dynalloca
invokes (#191616)
---
llvm/lib/Target/X86/X86ISelLowering.cpp | 4 ++--
llvm/test/DebugInfo/X86/instr-ref-dyn-alloca.ll | 13 +++++++------
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index edbc307c05166..cc9a138a5b120 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -26599,8 +26599,8 @@ X86TargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op,
} 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));
+ DAG.getTargetConstant(
+ Alignment ? Alignment->value() : 0, dl, MVT::i32));
Chain = Result.getValue(1);
MF.getInfo<X86MachineFunctionInfo>()->setHasDynAlloca(true);
} else {
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"
>From 77c265a83a50df59d2f8c01d603dcfb95ae79d5d Mon Sep 17 00:00:00 2001
From: "yuxuan.ding" <yuxuan.ding at mihoyo.com>
Date: Thu, 16 Apr 2026 17:13:18 +0800
Subject: [PATCH 3/3] fixup! [X86][WinEH] Avoid stale RSP after Win64 dynalloca
invokes (#191616)
---
.../CodeGen/X86/win64_alloca_dynalloca.ll | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/llvm/test/CodeGen/X86/win64_alloca_dynalloca.ll b/llvm/test/CodeGen/X86/win64_alloca_dynalloca.ll
index a82a7e4474abf..5787c79bf9edd 100644
--- a/llvm/test/CodeGen/X86/win64_alloca_dynalloca.ll
+++ b/llvm/test/CodeGen/X86/win64_alloca_dynalloca.ll
@@ -194,5 +194,26 @@ entry:
ret void
}
+define i64 @aligned_stack_args(i64 %n, i64 %x) 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 undef, 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
More information about the llvm-commits
mailing list