Index: docs/LangRef.rst
===================================================================
--- docs/LangRef.rst
+++ docs/LangRef.rst
@@ -1240,6 +1240,15 @@
``sanitize_thread``
This attribute indicates that ThreadSanitizer checks
(dynamic thread safety analysis) are enabled for this function.
+``stackprobe``
+ This attribute indicates that the function should emit stack
+ probes when the stack frame is larger than the guard page of the
+ target's stack. This ensures the function will be unable to overflow
+ the stack without it being detected.
+
+ If a function that has a ``stackprobe`` attribute is inlined into a
+ function that doesn't have a ``stackprobe`` attribute, then the resulting
+ function will have a ``stackprobe`` attribute.
``ssp``
This attribute indicates that the function should emit a stack
smashing protector. It is in the form of a "canary" --- a random value
Index: include/llvm-c/Core.h
===================================================================
--- include/llvm-c/Core.h
+++ include/llvm-c/Core.h
@@ -169,6 +169,7 @@
LLVMNonNullAttribute = 1ULL << 37,
LLVMJumpTableAttribute = 1ULL << 38,
LLVMDereferenceableAttribute = 1ULL << 39,
+ LLVMStackProbe = 1ULL << 40,
*/
} LLVMAttribute;
Index: include/llvm/Bitcode/LLVMBitCodes.h
===================================================================
--- include/llvm/Bitcode/LLVMBitCodes.h
+++ include/llvm/Bitcode/LLVMBitCodes.h
@@ -376,7 +376,8 @@
ATTR_KIND_IN_ALLOCA = 38,
ATTR_KIND_NON_NULL = 39,
ATTR_KIND_JUMP_TABLE = 40,
- ATTR_KIND_DEREFERENCEABLE = 41
+ ATTR_KIND_DEREFERENCEABLE = 41,
+ ATTR_KIND_STACK_PROBE = 42
};
enum ComdatSelectionKindCodes {
Index: include/llvm/IR/Attributes.h
===================================================================
--- include/llvm/IR/Attributes.h
+++ include/llvm/IR/Attributes.h
@@ -103,6 +103,7 @@
///< stored as log2 of alignment with +1 bias 0
///< means unaligned (different from
///< alignstack=(1))
+ StackProbe, ///< Insert stack probes.
StackProtect, ///< Stack protection.
StackProtectReq, ///< Stack protection required.
StackProtectStrong, ///< Strong Stack protection.
Index: lib/AsmParser/LLLexer.cpp
===================================================================
--- lib/AsmParser/LLLexer.cpp
+++ lib/AsmParser/LLLexer.cpp
@@ -636,6 +636,7 @@
KEYWORD(returns_twice);
KEYWORD(signext);
KEYWORD(sret);
+ KEYWORD(stackprobe);
KEYWORD(ssp);
KEYWORD(sspreq);
KEYWORD(sspstrong);
Index: lib/AsmParser/LLParser.cpp
===================================================================
--- lib/AsmParser/LLParser.cpp
+++ lib/AsmParser/LLParser.cpp
@@ -1033,6 +1033,7 @@
case lltok::kw_readnone: B.addAttribute(Attribute::ReadNone); break;
case lltok::kw_readonly: B.addAttribute(Attribute::ReadOnly); break;
case lltok::kw_returns_twice: B.addAttribute(Attribute::ReturnsTwice); break;
+ case lltok::kw_stackprobe: B.addAttribute(Attribute::StackProbe); break;
case lltok::kw_ssp: B.addAttribute(Attribute::StackProtect); break;
case lltok::kw_sspreq: B.addAttribute(Attribute::StackProtectReq); break;
case lltok::kw_sspstrong: B.addAttribute(Attribute::StackProtectStrong); break;
@@ -1334,6 +1335,7 @@
case lltok::kw_sanitize_address:
case lltok::kw_sanitize_memory:
case lltok::kw_sanitize_thread:
+ case lltok::kw_stackprobe:
case lltok::kw_ssp:
case lltok::kw_sspreq:
case lltok::kw_sspstrong:
@@ -1403,6 +1405,7 @@
case lltok::kw_sanitize_address:
case lltok::kw_sanitize_memory:
case lltok::kw_sanitize_thread:
+ case lltok::kw_stackprobe:
case lltok::kw_ssp:
case lltok::kw_sspreq:
case lltok::kw_sspstrong:
Index: lib/AsmParser/LLToken.h
===================================================================
--- lib/AsmParser/LLToken.h
+++ lib/AsmParser/LLToken.h
@@ -129,6 +129,7 @@
kw_returned,
kw_returns_twice,
kw_signext,
+ kw_stackprobe,
kw_ssp,
kw_sspreq,
kw_sspstrong,
Index: lib/Bitcode/Reader/BitcodeReader.cpp
===================================================================
--- lib/Bitcode/Reader/BitcodeReader.cpp
+++ lib/Bitcode/Reader/BitcodeReader.cpp
@@ -612,6 +612,8 @@
return Attribute::SExt;
case bitc::ATTR_KIND_STACK_ALIGNMENT:
return Attribute::StackAlignment;
+ case bitc::ATTR_KIND_STACK_PROBE:
+ return Attribute::StackProbe;
case bitc::ATTR_KIND_STACK_PROTECT:
return Attribute::StackProtect;
case bitc::ATTR_KIND_STACK_PROTECT_REQ:
Index: lib/Bitcode/Writer/BitcodeWriter.cpp
===================================================================
--- lib/Bitcode/Writer/BitcodeWriter.cpp
+++ lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -220,6 +220,8 @@
return bitc::ATTR_KIND_S_EXT;
case Attribute::StackAlignment:
return bitc::ATTR_KIND_STACK_ALIGNMENT;
+ case Attribute::StackProbe:
+ return bitc::ATTR_KIND_STACK_PROBE;
case Attribute::StackProtect:
return bitc::ATTR_KIND_STACK_PROTECT;
case Attribute::StackProtectReq:
Index: lib/IR/Attributes.cpp
===================================================================
--- lib/IR/Attributes.cpp
+++ lib/IR/Attributes.cpp
@@ -231,6 +231,8 @@
return "returns_twice";
if (hasAttribute(Attribute::SExt))
return "signext";
+ if (hasAttribute(Attribute::StackProbe))
+ return "stackprobe";
if (hasAttribute(Attribute::StackProtect))
return "ssp";
if (hasAttribute(Attribute::StackProtectReq))
@@ -426,6 +428,7 @@
case Attribute::InAlloca: return 1ULL << 43;
case Attribute::NonNull: return 1ULL << 44;
case Attribute::JumpTable: return 1ULL << 45;
+ case Attribute::StackProbe: return 1ULL << 46;
case Attribute::Dereferenceable:
llvm_unreachable("dereferenceable attribute not supported in raw format");
}
Index: lib/IR/Verifier.cpp
===================================================================
--- lib/IR/Verifier.cpp
+++ lib/IR/Verifier.cpp
@@ -755,6 +755,7 @@
I->getKindAsEnum() == Attribute::NoInline ||
I->getKindAsEnum() == Attribute::AlwaysInline ||
I->getKindAsEnum() == Attribute::OptimizeForSize ||
+ I->getKindAsEnum() == Attribute::StackProbe ||
I->getKindAsEnum() == Attribute::StackProtect ||
I->getKindAsEnum() == Attribute::StackProtectReq ||
I->getKindAsEnum() == Attribute::StackProtectStrong ||
Index: lib/Target/CppBackend/CPPBackend.cpp
===================================================================
--- lib/Target/CppBackend/CPPBackend.cpp
+++ lib/Target/CppBackend/CPPBackend.cpp
@@ -507,6 +507,7 @@
HANDLE_ATTR(AlwaysInline);
HANDLE_ATTR(OptimizeNone);
HANDLE_ATTR(OptimizeForSize);
+ HANDLE_ATTR(StackProbe);
HANDLE_ATTR(StackProtect);
HANDLE_ATTR(StackProtectReq);
HANDLE_ATTR(StackProtectStrong);
Index: lib/Target/X86/X86FrameLowering.cpp
===================================================================
--- lib/Target/X86/X86FrameLowering.cpp
+++ lib/Target/X86/X86FrameLowering.cpp
@@ -663,47 +663,48 @@
// responsible for adjusting the stack pointer. Touching the stack at 4K
// increments is necessary to ensure that the guard pages used by the OS
// virtual memory manager are allocated in correct sequence.
- if (NumBytes >= 4096 && STI.isOSWindows() && !STI.isTargetMacho()) {
- const char *StackProbeSymbol;
- if (Is64Bit) {
- if (STI.isTargetCygMing()) {
- StackProbeSymbol = "___chkstk_ms";
- } else {
- StackProbeSymbol = "__chkstk";
- }
- } else if (STI.isTargetCygMing())
- StackProbeSymbol = "_alloca";
- else
- StackProbeSymbol = "_chkstk";
+ if (NumBytes >= 4096 && ((STI.isOSWindows() && !STI.isTargetMacho()) ||
+ Fn->getAttributes()
+ .hasAttribute(AttributeSet::FunctionIndex,
+ Attribute::StackProbe))) {
+ const char *StackProbeSymbol;
- // Check whether EAX is livein for this function.
- bool isEAXAlive = isEAXLiveIn(MF);
+ if (STI.isOSWindows()) {
+ if (Is64Bit) {
+ if (STI.isTargetCygMing()) {
+ StackProbeSymbol = "___chkstk_ms";
+ } else {
+ StackProbeSymbol = "__chkstk";
+ }
+ } else if (STI.isTargetCygMing())
+ StackProbeSymbol = "_alloca";
+ else
+ StackProbeSymbol = "_chkstk";
+ } else {
+ StackProbeSymbol = "__probestack";
+ }
- if (isEAXAlive) {
- // Sanity check that EAX is not livein for this function.
- // It should not be, so throw an assert.
- assert(!Is64Bit && "EAX is livein in x64 case!");
+ // Check whether the accumulator register is livein for this function.
+ bool isRegAccAlive = isEAXLiveIn(MF);
+ auto RegAcc = Is64Bit ? X86::RAX : X86::EAX;
- // Save EAX
- BuildMI(MBB, MBBI, DL, TII.get(X86::PUSH32r))
- .addReg(X86::EAX, RegState::Kill)
+ if (isRegAccAlive) {
+ // Save RegAcc
+ BuildMI(MBB, MBBI, DL, TII.get(Is64Bit ? X86::PUSH64r : X86::PUSH32r))
+ .addReg(RegAcc, RegState::Kill)
.setMIFlag(MachineInstr::FrameSetup);
}
- if (Is64Bit) {
- // Handle the 64-bit Windows ABI case where we need to call __chkstk.
- // Function prologue is responsible for adjusting the stack pointer.
- BuildMI(MBB, MBBI, DL, TII.get(X86::MOV64ri), X86::RAX)
- .addImm(NumBytes)
- .setMIFlag(MachineInstr::FrameSetup);
- } else {
- // Allocate NumBytes-4 bytes on stack in case of isEAXAlive.
- // We'll also use 4 already allocated bytes for EAX.
- BuildMI(MBB, MBBI, DL, TII.get(X86::MOV32ri), X86::EAX)
- .addImm(isEAXAlive ? NumBytes - 4 : NumBytes)
- .setMIFlag(MachineInstr::FrameSetup);
- }
+ uint64_t NumBytesAdj = isRegAccAlive ? NumBytes - (Is64Bit ? 8 : 4) :
+ NumBytes;
+
+ // Allocate NumBytesAdj bytes on stack in case of isRegAAlive.
+ // We'll also use 8/4 already allocated bytes for EAX.
+ BuildMI(MBB, MBBI, DL, TII.get(Is64Bit ? X86::MOV64ri : X86::MOV32ri),
+ RegAcc)
+ .addImm(NumBytesAdj)
+ .setMIFlag(MachineInstr::FrameSetup);
BuildMI(MBB, MBBI, DL,
TII.get(Is64Bit ? X86::W64ALLOCA : X86::CALLpcrel32))
@@ -712,20 +713,22 @@
.addReg(X86::EFLAGS, RegState::Define | RegState::Implicit)
.setMIFlag(MachineInstr::FrameSetup);
- if (Is64Bit) {
+ if (Is64Bit || !STI.isOSWindows()) {
// MSVC x64's __chkstk and cygwin/mingw's ___chkstk_ms do not adjust %rsp
// themself. It also does not clobber %rax so we can reuse it when
// adjusting %rsp.
- BuildMI(MBB, MBBI, DL, TII.get(X86::SUB64rr), StackPtr)
+ BuildMI(MBB, MBBI, DL, TII.get(Is64Bit ? X86::SUB64rr : X86::SUB32rr),
+ StackPtr)
.addReg(StackPtr)
- .addReg(X86::RAX)
+ .addReg(RegAcc)
.setMIFlag(MachineInstr::FrameSetup);
}
- if (isEAXAlive) {
- // Restore EAX
- MachineInstr *MI = addRegOffset(BuildMI(MF, DL, TII.get(X86::MOV32rm),
- X86::EAX),
- StackPtr, false, NumBytes - 4);
+ if (isRegAccAlive) {
+ // Restore RegAcc
+ auto MIB = BuildMI(MF, DL,
+ TII.get(Is64Bit ? X86::MOV64rm : X86::MOV32rm),
+ RegAcc);
+ MachineInstr *MI = addRegOffset(MIB, StackPtr, false, NumBytesAdj);
MI->setFlag(MachineInstr::FrameSetup);
MBB.insert(MBBI, MI);
}
Index: lib/Transforms/IPO/Inliner.cpp
===================================================================
--- lib/Transforms/IPO/Inliner.cpp
+++ lib/Transforms/IPO/Inliner.cpp
@@ -137,6 +137,11 @@
AdjustCallerSSPLevel(Caller, Callee);
+ if (Callee->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
+ Attribute::StackProbe)) {
+ Caller->addFnAttr(Attribute::StackProbe);
+ }
+
// Look at all of the allocas that we inlined through this call site. If we
// have already inlined other allocas through other calls into this function,
// then we know that they have disjoint lifetimes and that we can merge them.
Index: test/Bitcode/attributes.ll
===================================================================
--- test/Bitcode/attributes.ll
+++ test/Bitcode/attributes.ll
@@ -204,7 +204,7 @@
; CHECK: define void @f34()
{
call void @nobuiltin() nobuiltin
-; CHECK: call void @nobuiltin() #25
+; CHECK: call void @nobuiltin() #26
ret void;
}
@@ -245,6 +245,12 @@
ret void
}
+define void @f42() stackprobe
+; CHECK: define void @f42() #25
+{
+ ret void;
+}
+
; CHECK: attributes #0 = { noreturn }
; CHECK: attributes #1 = { nounwind }
; CHECK: attributes #2 = { readnone }
@@ -270,4 +276,5 @@
; CHECK: attributes #22 = { minsize }
; CHECK: attributes #23 = { noinline optnone }
; CHECK: attributes #24 = { jumptable }
-; CHECK: attributes #25 = { nobuiltin }
+; CHECK: attributes #25 = { stackprobe }
+; CHECK: attributes #26 = { nobuiltin }
Index: test/CodeGen/X86/stack-probes.ll
===================================================================
--- /dev/null
+++ test/CodeGen/X86/stack-probes.ll
@@ -0,0 +1,30 @@
+; RUN: llc -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck --check-prefix=X86-Linux %s
+; RUN: llc -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck --check-prefix=X64-Linux %s
+
+declare void @use([4096 x i8]*)
+
+define void @test() stackprobe {
+ %array = alloca [4096 x i8], align 16
+ call void @use([4096 x i8]* %array)
+ ret void
+
+; X86-Linux-LABEL: test:
+; X86-Linux: movl $4124, %eax
+; X86-Linux-NEXT: calll __probestack
+; X86-Linux-NEXT: subl %eax, %esp
+; X86-Linux: leal 16(%esp), %eax
+; X86-Linux-NEXT: movl %eax, (%esp)
+; X86-Linux-NEXT: calll use
+; X86-Linux-NEXT: addl $4124, %esp
+; X86-Linux-NEXT: retl
+
+; X64-Linux-LABEL: test:
+; X64-Linux: movabsq $4104, %rax
+; X64-Linux-NEXT: callq __probestack
+; X64-Linux-NEXT: subq %rax, %rsp
+; X64-Linux: leaq (%rsp), %rdi
+; X64-Linux-NEXT: callq use
+; X64-Linux-NEXT: addq $4104, %rsp
+; X64-Linux-NEXT: retq
+
+}
Index: utils/kate/llvm.xml
===================================================================
--- utils/kate/llvm.xml
+++ utils/kate/llvm.xml
@@ -86,6 +86,7 @@
- optsize
- readnone
- readonly
+ - stackprobe
- ssp
- sspreq
- sspstrong
Index: utils/vim/llvm.vim
===================================================================
--- utils/vim/llvm.vim
+++ utils/vim/llvm.vim
@@ -56,7 +56,7 @@
syn keyword llvmKeyword tail target thread_local to triple unnamed_addr
syn keyword llvmKeyword unordered uwtable volatile weak weak_odr
syn keyword llvmKeyword x86_fastcallcc x86_stdcallcc x86_thiscallcc x86_64_sysvcc
-syn keyword llvmKeyword x86_64_win64cc zeroext
+syn keyword llvmKeyword x86_64_win64cc zeroext stackprobe
" Obsolete keywords.
syn keyword llvmError getresult begin end