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