Skip to content
This repository was archived by the owner on Feb 5, 2019. It is now read-only.

Commit 4173c1f

Browse files
alexcrichtonarielb1
authored andcommitted
Merge pull request #86 from alexcrichton/probestack
Add x86 probestack support
2 parents 4a6869c + db54cdb commit 4173c1f

File tree

10 files changed

+134
-19
lines changed

10 files changed

+134
-19
lines changed

docs/LangRef.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,6 +1467,19 @@ example:
14671467
This attribute by itself does not imply restrictions on
14681468
inter-procedural optimizations. All of the semantic effects the
14691469
patching may have to be separately conveyed via the linkage type.
1470+
``"probe-stack"``
1471+
This attribute indicates that the function will trigger a guard region
1472+
in the end of the stack. It ensures that accesses to the stack must be
1473+
no further apart than the size of the guard region to a previous
1474+
access of the stack. It takes one required string value, the name of
1475+
the stack probing function that will be called.
1476+
1477+
If a function that has a ``"probe-stack"`` attribute is inlined into
1478+
a function with another ``"probe-stack"`` attribute, the resulting
1479+
function has the ``"probe-stack"`` attribute of the caller. If a
1480+
function that has a ``"probe-stack"`` attribute is inlined into a
1481+
function that has no ``"probe-stack"`` attribute at all, the resulting
1482+
function has the ``"probe-stack"`` attribute of the callee.
14701483
``readnone``
14711484
On a function, this attribute indicates that the function computes its
14721485
result (or decides to unwind an exception) based strictly on its arguments,

include/llvm/IR/Attributes.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,4 @@ def : MergeRule<"setAND<UnsafeFPMathAttr>">;
211211
def : MergeRule<"setOR<NoImplicitFloatAttr>">;
212212
def : MergeRule<"setOR<NoJumpTablesAttr>">;
213213
def : MergeRule<"adjustCallerSSPLevel">;
214+
def : MergeRule<"adjustCallerStackProbes">;

include/llvm/Target/TargetLowering.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,12 @@ class TargetLoweringBase {
11791179
/// Returns the target-specific address of the unsafe stack pointer.
11801180
virtual Value *getSafeStackPointerLocation(IRBuilder<> &IRB) const;
11811181

1182+
/// Returns the name of the symbol used to emit stack probes or the empty
1183+
/// string if not applicable.
1184+
virtual StringRef getStackProbeSymbolName(MachineFunction &MF) const {
1185+
return "";
1186+
}
1187+
11821188
/// Returns true if a cast between SrcAS and DestAS is a noop.
11831189
virtual bool isNoopAddrSpaceCast(unsigned SrcAS, unsigned DestAS) const {
11841190
return false;

lib/IR/Attributes.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,6 +1502,13 @@ static void adjustCallerSSPLevel(Function &Caller, const Function &Callee) {
15021502
Caller.addFnAttr(Attribute::StackProtect);
15031503
}
15041504

1505+
/// \brief If the inlined function required stack probes, then ensure that
1506+
/// the calling function has those too.
1507+
static void adjustCallerStackProbes(Function &Caller, const Function &Callee) {
1508+
if (!Caller.hasFnAttribute("probe-stack") && Callee.hasFnAttribute("probe-stack"))
1509+
Caller.addFnAttr("probe-stack", Callee.getFnAttribute("probe-stack").getValueAsString());
1510+
}
1511+
15051512
#define GET_ATTR_COMPAT_FUNC
15061513
#include "AttributesCompatFunc.inc"
15071514

lib/Target/X86/X86FrameLowering.cpp

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -719,17 +719,7 @@ void X86FrameLowering::emitStackProbeCall(MachineFunction &MF,
719719
else
720720
CallOp = X86::CALLpcrel32;
721721

722-
const char *Symbol;
723-
if (Is64Bit) {
724-
if (STI.isTargetCygMing()) {
725-
Symbol = "___chkstk_ms";
726-
} else {
727-
Symbol = "__chkstk";
728-
}
729-
} else if (STI.isTargetCygMing())
730-
Symbol = "_alloca";
731-
else
732-
Symbol = "_chkstk";
722+
StringRef Symbol = STI.getTargetLowering()->getStackProbeSymbolName(MF);
733723

734724
MachineInstrBuilder CI;
735725
MachineBasicBlock::iterator ExpansionMBBI = std::prev(MBBI);
@@ -740,10 +730,11 @@ void X86FrameLowering::emitStackProbeCall(MachineFunction &MF,
740730
// For the large code model, we have to call through a register. Use R11,
741731
// as it is scratch in all supported calling conventions.
742732
BuildMI(MBB, MBBI, DL, TII.get(X86::MOV64ri), X86::R11)
743-
.addExternalSymbol(Symbol);
733+
.addExternalSymbol(MF.createExternalSymbolName(Symbol));
744734
CI = BuildMI(MBB, MBBI, DL, TII.get(CallOp)).addReg(X86::R11);
745735
} else {
746-
CI = BuildMI(MBB, MBBI, DL, TII.get(CallOp)).addExternalSymbol(Symbol);
736+
CI = BuildMI(MBB, MBBI, DL, TII.get(CallOp))
737+
.addExternalSymbol(MF.createExternalSymbolName(Symbol));
747738
}
748739

749740
unsigned AX = Is64Bit ? X86::RAX : X86::EAX;
@@ -754,13 +745,13 @@ void X86FrameLowering::emitStackProbeCall(MachineFunction &MF,
754745
.addReg(SP, RegState::Define | RegState::Implicit)
755746
.addReg(X86::EFLAGS, RegState::Define | RegState::Implicit);
756747

757-
if (Is64Bit) {
748+
if (!STI.isTargetWin32()) {
758749
// MSVC x64's __chkstk and cygwin/mingw's ___chkstk_ms do not adjust %rsp
759750
// themselves. It also does not clobber %rax so we can reuse it when
760751
// adjusting %rsp.
761-
BuildMI(MBB, MBBI, DL, TII.get(X86::SUB64rr), X86::RSP)
762-
.addReg(X86::RSP)
763-
.addReg(X86::RAX);
752+
BuildMI(MBB, MBBI, DL, TII.get(getSUBrrOpcode(Is64Bit)), SP)
753+
.addReg(SP)
754+
.addReg(AX);
764755
}
765756

766757
if (InProlog) {
@@ -949,7 +940,8 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
949940
X86FI->setCalleeSavedFrameSize(
950941
X86FI->getCalleeSavedFrameSize() - TailCallReturnAddrDelta);
951942

952-
bool UseStackProbe = (STI.isOSWindows() && !STI.isTargetMachO());
943+
bool UseRedZone = false;
944+
bool UseStackProbe = !STI.getTargetLowering()->getStackProbeSymbolName(MF).empty();
953945

954946
// The default stack probe size is 4096 if the function has no stackprobesize
955947
// attribute.
@@ -968,6 +960,7 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
968960
!TRI->needsStackRealignment(MF) &&
969961
!MFI.hasVarSizedObjects() && // No dynamic alloca.
970962
!MFI.adjustsStack() && // No calls.
963+
!UseStackProbe && // No stack probes.
971964
!IsWin64CC && // Win64 has no Red Zone
972965
!MFI.hasCopyImplyingStackAdjustment() && // Don't push and pop.
973966
!MF.shouldSplitStack()) { // Regular stack
@@ -976,6 +969,7 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
976969
X86FI->setUsesRedZone(MinSize > 0 || StackSize > 0);
977970
StackSize = std::max(MinSize, StackSize > 128 ? StackSize - 128 : 0);
978971
MFI.setStackSize(StackSize);
972+
UseRedZone = true;
979973
}
980974

981975
// Insert stack pointer adjustment for later moving of return addr. Only
@@ -1158,6 +1152,8 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
11581152
if (IsWin64Prologue && !IsFunclet && TRI->needsStackRealignment(MF))
11591153
AlignedNumBytes = alignTo(AlignedNumBytes, MaxAlign);
11601154
if (AlignedNumBytes >= StackProbeSize && UseStackProbe) {
1155+
assert(!UseRedZone && "The Red Zone is not accounted for in stack probes");
1156+
11611157
// Check whether EAX is livein for this block.
11621158
bool isEAXAlive = isEAXLiveIn(MBB);
11631159

lib/Target/X86/X86ISelLowering.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18272,8 +18272,9 @@ X86TargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op,
1827218272
SelectionDAG &DAG) const {
1827318273
MachineFunction &MF = DAG.getMachineFunction();
1827418274
bool SplitStack = MF.shouldSplitStack();
18275+
bool EmitStackProbe = !getStackProbeSymbolName(MF).empty();
1827518276
bool Lower = (Subtarget.isOSWindows() && !Subtarget.isTargetMachO()) ||
18276-
SplitStack;
18277+
SplitStack || EmitStackProbe;
1827718278
SDLoc dl(Op);
1827818279

1827918280
// Get the inputs.
@@ -34918,3 +34919,22 @@ void X86TargetLowering::insertCopiesSplitCSR(
3491834919
bool X86TargetLowering::supportSwiftError() const {
3491934920
return Subtarget.is64Bit();
3492034921
}
34922+
34923+
/// Returns the name of the symbol used to emit stack probes or the empty
34924+
/// string if not applicable.
34925+
StringRef X86TargetLowering::getStackProbeSymbolName(MachineFunction &MF) const {
34926+
// If the function specifically requests stack probes, emit them.
34927+
if (MF.getFunction()->hasFnAttribute("probe-stack"))
34928+
return MF.getFunction()->getFnAttribute("probe-stack").getValueAsString();
34929+
34930+
// Generally, if we aren't on Windows, the platform ABI does not include
34931+
// support for stack probes, so don't emit them.
34932+
if (!Subtarget.isOSWindows() || Subtarget.isTargetMachO())
34933+
return "";
34934+
34935+
// We need a stack probe to conform to the Windows ABI. Choose the right
34936+
// symbol.
34937+
if (Subtarget.is64Bit())
34938+
return Subtarget.isTargetCygMing() ? "___chkstk_ms" : "__chkstk";
34939+
return Subtarget.isTargetCygMing() ? "_alloca" : "_chkstk";
34940+
}

lib/Target/X86/X86ISelLowering.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,8 @@ namespace llvm {
10421042

10431043
bool supportSwiftError() const override;
10441044

1045+
StringRef getStackProbeSymbolName(MachineFunction &MF) const override;
1046+
10451047
unsigned getMaxSupportedInterleaveFactor() const override { return 4; }
10461048

10471049
/// \brief Lower interleaved load(s) into target specific
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
; RUN: llc -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s
2+
3+
; Ensure that red zone usage occurs.
4+
define void @testStackProbesOff() {
5+
%array = alloca [40096 x i8], align 16
6+
ret void
7+
8+
; CHECK-LABEL: testStackProbesOff:
9+
; CHECK: subq $39976, %rsp # imm = 0x9C28
10+
}
11+
12+
; Ensure stack probes do not result in red zone usage.
13+
define void @testStackProbesOn() "probe-stack"="__probestack" {
14+
%array = alloca [40096 x i8], align 16
15+
ret void
16+
17+
; CHECK-LABEL: testStackProbesOn:
18+
; CHECK: movl $40104, %eax # imm = 0x9CA8
19+
; CHECK-NEXT: callq __probestack
20+
; CHECK-NEXT: subq %rax, %rsp
21+
}

test/CodeGen/X86/stack-probes.ll

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
; RUN: llc -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck --check-prefix=X86-LINUX %s
2+
; RUN: llc -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck --check-prefix=X64-LINUX %s
3+
; RUN: llc -mtriple=x86_64-pc-linux-gnu -code-model=large < %s -o - | FileCheck --check-prefix=X64-LINUX-LARGE %s
4+
5+
declare void @use([40096 x i8]*)
6+
7+
; Ensure calls to __probestack occur for large stack frames
8+
define void @test() "probe-stack"="__probestack" {
9+
%array = alloca [40096 x i8], align 16
10+
call void @use([40096 x i8]* %array)
11+
ret void
12+
13+
; X86-LINUX-LABEL: test:
14+
; X86-LINUX: movl $40124, %eax # imm = 0x9CBC
15+
; X86-LINUX-NEXT: calll __probestack
16+
; X86-LINUX-NEXT: subl %eax, %esp
17+
18+
; X64-LINUX-LABEL: test:
19+
; X64-LINUX: movl $40104, %eax # imm = 0x9CA8
20+
; X64-LINUX-NEXT: callq __probestack
21+
; X64-LINUX-NEXT: subq %rax, %rsp
22+
23+
; X64-LINUX-LARGE-LABEL: test:
24+
; X64-LINUX-LARGE: movl $40104, %eax # imm = 0x9CA8
25+
; X64-LINUX-LARGE-NEXT: movabsq $__probestack, %r11
26+
; X64-LINUX-LARGE-NEXT: callq *%r11
27+
; X64-LINUX-LARGE-NEXT: subq %rax, %rsp
28+
29+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
; RUN: opt %s -inline -S | FileCheck %s
2+
3+
define internal void @inner() "probe-stack"="__probestackinner" {
4+
ret void
5+
}
6+
7+
define void @outerNoAttribute() {
8+
call void @inner()
9+
ret void
10+
}
11+
12+
define void @outerConflictingAttribute() "probe-stack"="__probestackouter" {
13+
call void @inner()
14+
ret void
15+
}
16+
17+
; CHECK: define void @outerNoAttribute() #0
18+
; CHECK: define void @outerConflictingAttribute() #1
19+
; CHECK: attributes #0 = { "probe-stack"="__probestackinner" }
20+
; CHECK: attributes #1 = { "probe-stack"="__probestackouter" }

0 commit comments

Comments
 (0)