diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1995,11 +1995,9 @@ Variables that are identified as requiring a protector will be arranged on the stack such that they are adjacent to the stack protector guard. - A function with the ``ssp`` attribute but without the ``alwaysinline`` - attribute cannot be inlined into a function without a - ``ssp/sspreq/sspstrong`` attribute. If inlined, the caller will get the - ``ssp`` attribute. ``call``, ``invoke``, and ``callbr`` instructions with - the ``alwaysinline`` attribute force inlining. + If a function with an ``ssp`` attribute is inlined into a calling function, + the attribute is not carried over to the calling function. + ``sspstrong`` This attribute indicates that the function should emit a stack smashing protector. This attribute causes a strong heuristic to be used when @@ -2024,12 +2022,10 @@ This overrides the ``ssp`` function attribute. - A function with the ``sspstrong`` attribute but without the - ``alwaysinline`` attribute cannot be inlined into a function without a - ``ssp/sspstrong/sspreq`` attribute. If inlined, the caller will get the - ``sspstrong`` attribute unless the ``sspreq`` attribute exists. ``call``, - ``invoke``, and ``callbr`` instructions with the ``alwaysinline`` attribute - force inlining. + If a function with an ``sspstrong`` attribute is inlined into a calling + function which has an ``ssp`` attribute, the calling function's attribute + will be upgraded to ``sspstrong``. + ``sspreq`` This attribute indicates that the function should *always* emit a stack smashing protector. This overrides the ``ssp`` and ``sspstrong`` function @@ -2046,11 +2042,9 @@ #. Variables that have had their address taken are 3rd closest to the protector. - A function with the ``sspreq`` attribute but without the ``alwaysinline`` - attribute cannot be inlined into a function without a - ``ssp/sspstrong/sspreq`` attribute. If inlined, the caller will get the - ``sspreq`` attribute. ``call``, ``invoke``, and ``callbr`` instructions - with the ``alwaysinline`` attribute force inlining. + If a function with an ``sspreq`` attribute is inlined into a calling + function which has an ``ssp`` or ``sspstrong`` attribute, the calling + function's attribute will be upgraded to ``sspreq``. ``strictfp`` This attribute indicates that the function was called from a scope that diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp --- a/llvm/lib/Analysis/InlineCost.cpp +++ b/llvm/lib/Analysis/InlineCost.cpp @@ -2898,15 +2898,6 @@ if (Call.isNoInline()) return InlineResult::failure("noinline call site attribute"); - // Don't inline functions if one does not have any stack protector attribute - // but the other does. - if (Caller->hasStackProtectorFnAttr() && !Callee->hasStackProtectorFnAttr()) - return InlineResult::failure( - "stack protected caller but callee requested no stack protector"); - if (Callee->hasStackProtectorFnAttr() && !Caller->hasStackProtectorFnAttr()) - return InlineResult::failure( - "stack protected callee but caller requested no stack protector"); - return None; } diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -1921,6 +1921,12 @@ /// If the inlined function had a higher stack protection level than the /// calling function, then bump up the caller's stack protection level. static void adjustCallerSSPLevel(Function &Caller, const Function &Callee) { + // If the calling function has *no* stack protection level (e.g. it was built + // with Clang's -fno-stack-protector or no_stack_protector attribute), don't + // change it as that could change the program's semantics. + if (!Caller.hasStackProtectorFnAttr()) + return; + // If upgrading the SSP attribute, clear out the old SSP Attributes first. // Having multiple SSP attributes doesn't actually hurt, but it adds useless // clutter to the IR. diff --git a/llvm/test/ThinLTO/X86/nossp.ll b/llvm/test/ThinLTO/X86/nossp.ll --- a/llvm/test/ThinLTO/X86/nossp.ll +++ b/llvm/test/ThinLTO/X86/nossp.ll @@ -23,7 +23,8 @@ ; nossp caller should be able to inline nossp callee. define void @nossp_caller() { -; CHECK-LABEL: @nossp_caller +; CHECK-LABEL: define void @nossp_caller() +; CHECK-NOT: #0 ; CHECK-NEXT: tail call void @foo tail call void @nossp_callee() ret void @@ -31,28 +32,34 @@ ; ssp caller should be able to inline ssp callee. define void @ssp_caller() ssp { -; CHECK-LABEL: @ssp_caller +; CHECK-LABEL: define void @ssp_caller() +; CHECK-SAME: #0 ; CHECK-NEXT: tail call void @foo tail call void @ssp_callee() ret void } -; nossp caller should *NOT* be able to inline ssp callee. +; nossp caller should be able to inline ssp callee. +; the ssp attribute is not propagated. define void @nossp_caller2() { -; CHECK-LABEL: @nossp_caller2 -; CHECK-NEXT: tail call void @ssp_callee +; CHECK-LABEL: define void @nossp_caller2() +; CHECK-NOT: #0 +; CHECK-NEXT: tail call void @foo tail call void @ssp_callee() ret void } -; ssp caller should *NOT* be able to inline nossp callee. +; ssp caller should be able to inline nossp callee. define void @ssp_caller2() ssp { -; CHECK-LABEL: @ssp_caller2 -; CHECK-NEXT: tail call void @nossp_callee +; CHECK-LABEL: define void @ssp_caller2() +; CHECK-SAME: #0 +; CHECK-NEXT: tail call void @foo tail call void @nossp_callee() ret void } +; CHECK: attributes #0 = { ssp } + ;--- b.ll target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-linux-gnu" diff --git a/llvm/test/Transforms/Inline/inline_nossp.ll b/llvm/test/Transforms/Inline/inline_nossp.ll deleted file mode 100644 --- a/llvm/test/Transforms/Inline/inline_nossp.ll +++ /dev/null @@ -1,50 +0,0 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -passes='cgscc(inline)' %s -S -pass-remarks-missed=inline 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-INLINE %s -; RUN: opt -passes=always-inline -o - -S %s | FileCheck %s - -; CHECK-INLINE: 'ssp' not inlined into 'nossp_caller' because it should never be inlined (cost=never): stack protected callee but caller requested no stack protector -; CHECK-INLINE: 'nossp' not inlined into 'ssp_caller' because it should never be inlined (cost=never): stack protected caller but callee requested no stack protector - -; Not interesting to test. -define i32 @nossp() { ret i32 41 } -define i32 @ssp() sspstrong { ret i32 42 } -define i32 @nossp_alwaysinline() alwaysinline { ret i32 43 } -define i32 @ssp_alwaysinline() sspstrong alwaysinline { ret i32 44 } - -; @ssp should not be inlined due to mismatch stack protector. -; @ssp_alwaysinline should be inlined due to alwaysinline. -define i32 @nossp_caller() { -; CHECK-LABEL: @nossp_caller( -; CHECK-NEXT: [[TMP1:%.*]] = call i32 @ssp() -; CHECK-NEXT: ret i32 44 -; - call i32 @ssp() - %2 = call i32 @ssp_alwaysinline() - ret i32 %2 -} -; @nossp should not be inlined due to mismatch stack protector. -; @nossp_alwaysinline should be inlined due to alwaysinline. -define i32 @ssp_caller() sspstrong { -; CHECK-LABEL: @ssp_caller( -; CHECK-NEXT: [[TMP1:%.*]] = call i32 @nossp() -; CHECK-NEXT: ret i32 43 -; - call i32 @nossp() - %2 = call i32 @nossp_alwaysinline() - ret i32 %2 -} - -; The alwaysinline attribute can also appear on the CallBase (ie. the call -; site), ie. when __attribute__((flatten)) is used on the caller. Treat this -; the same as if the caller had the fn attr alwaysinline and permit inline -; substitution, despite the mismatch between caller and callee on ssp attrs. -; -; Curiously, the always_inline attribute on a CallInst is only expanded by the -; inline pass, but not always_inline pass! -define i32 @nossp_alwaysinline_caller() { -; CHECK-INLINE-LABEL: @nossp_alwaysinline_caller( -; CHECK-INLINE-NEXT: ret i32 42 -; - %1 = call i32 @ssp() alwaysinline - ret i32 %1 -} diff --git a/llvm/test/Transforms/Inline/inline_ssp.ll b/llvm/test/Transforms/Inline/inline_ssp.ll --- a/llvm/test/Transforms/Inline/inline_ssp.ll +++ b/llvm/test/Transforms/Inline/inline_ssp.ll @@ -9,15 +9,23 @@ ; These first four functions (@fun_sspreq, @fun_sspstrong, @fun_ssp, @fun_nossp) ; are used by the remaining functions to ensure that the SSP attributes are -; propagated correctly. The caller should have its SSP attribute set as: +; propagated correctly. If the caller had an SSP attribute before inlining, it +; should have its new SSP attribute set as: ; strictest(caller-ssp-attr, callee-ssp-attr), where strictness is ordered as: -; sspreq > sspstrong > ssp > [no ssp] +; sspreq > sspstrong > ssp + define internal void @fun_sspreq() sspreq { entry: %call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str3, i32 0, i32 0)) ret void } +define internal void @fun_sspreq_alwaysinline() sspreq alwaysinline { +entry: + %call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str3, i32 0, i32 0)) + ret void +} + define internal void @fun_sspstrong() sspstrong { entry: %call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str2, i32 0, i32 0)) @@ -66,6 +74,13 @@ ret void } +define void @alwaysinline_req_nossp() { +entry: +; CHECK: @alwaysinline_req_nossp() { + call void @fun_sspreq_alwaysinline() + ret void +} + define void @inline_strong_req() sspreq { entry: ; CHECK: @inline_strong_req() #[[SSPREQ]]