[llvm] bc044a8 - [Inline] prevent inlining on stack protector mismatch

Nick Desaulniers via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 2 11:00:41 PST 2020


Author: Nick Desaulniers
Date: 2020-12-02T11:00:16-08:00
New Revision: bc044a88ee3c16e4164740732a7adc91a29b24f5

URL: https://github.com/llvm/llvm-project/commit/bc044a88ee3c16e4164740732a7adc91a29b24f5
DIFF: https://github.com/llvm/llvm-project/commit/bc044a88ee3c16e4164740732a7adc91a29b24f5.diff

LOG: [Inline] prevent inlining on stack protector mismatch

It's common for code that manipulates the stack via inline assembly or
that has to set up its own stack canary (such as the Linux kernel) would
like to avoid stack protectors in certain functions. In this case, we've
been bitten by numerous bugs where a callee with a stack protector is
inlined into an attribute((no_stack_protector)) caller, which
generally breaks the caller's assumptions about not having a stack
protector. LTO exacerbates the issue.

While developers can avoid this by putting all no_stack_protector
functions in one translation unit together and compiling those with
-fno-stack-protector, it's generally not very ergonomic or as
ergonomic as a function attribute, and still doesn't work for LTO. See also:
https://lore.kernel.org/linux-pm/20200915172658.1432732-1-rkir@google.com/
https://lore.kernel.org/lkml/20200918201436.2932360-30-samitolvanen@google.com/T/#u

SSP attributes can be ordered by strength. Weakest to strongest, they
are: ssp, sspstrong, sspreq.  Callees with differing SSP attributes may be
inlined into each other, and the strongest attribute will be applied to the
caller. (No change)

After this change:
* A callee with no SSP attributes will no longer be inlined into a
  caller with SSP attributes.
* The reverse is also true: a callee with an SSP attribute will not be
  inlined into a caller with no SSP attributes.
* The alwaysinline attribute overrides these rules.

Functions that get synthesized by the compiler may not get inlined as a
result if they are not created with the same stack protector function
attribute as their callers.

Alternative approach to https://reviews.llvm.org/D87956.

Fixes pr/47479.

Signed-off-by: Nick Desaulniers <ndesaulniers at google.com>

Reviewed By: rnk, MaskRay

Differential Revision: https://reviews.llvm.org/D91816

Added: 
    llvm/test/ThinLTO/X86/nossp.ll
    llvm/test/Transforms/Inline/inline_nossp.ll

Modified: 
    llvm/include/llvm/IR/Function.h
    llvm/lib/Analysis/InlineCost.cpp
    llvm/lib/CodeGen/StackProtector.cpp
    llvm/lib/IR/Attributes.cpp
    llvm/lib/IR/Function.cpp
    llvm/test/CodeGen/AArch64/stack-guard-remat-bitcast.ll
    llvm/test/CodeGen/X86/stack-protector-2.ll
    llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll
    llvm/test/Transforms/Inline/devirtualize.ll
    llvm/test/Transforms/Inline/inline-byval-bonus.ll
    llvm/test/Transforms/Inline/inline_ssp.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h
index 1736b3ed0363..cf61db1d3347 100644
--- a/llvm/include/llvm/IR/Function.h
+++ b/llvm/include/llvm/IR/Function.h
@@ -381,6 +381,9 @@ class Function : public GlobalObject, public ilist_node<Function> {
   void setGC(std::string Str);
   void clearGC();
 
+  /// Returns true if the function has ssp, sspstrong, or sspreq fn attrs.
+  bool hasStackProtectorFnAttr() const;
+
   /// adds the attribute to the list of attributes.
   void addAttribute(unsigned i, Attribute::AttrKind Kind);
 

diff  --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp
index cb731f6a3a66..077b05edcd13 100644
--- a/llvm/lib/Analysis/InlineCost.cpp
+++ b/llvm/lib/Analysis/InlineCost.cpp
@@ -2382,6 +2382,15 @@ Optional<InlineResult> llvm::getAttributeBasedInliningDecision(
   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;
 }
 
@@ -2441,7 +2450,8 @@ InlineResult llvm::isInlineViable(Function &F) {
         continue;
 
       // Disallow recursive calls.
-      if (&F == Call->getCalledFunction())
+      Function *Callee = Call->getCalledFunction();
+      if (&F == Callee)
         return InlineResult::failure("recursive call");
 
       // Disallow calls which expose returns-twice to a function not previously
@@ -2450,8 +2460,8 @@ InlineResult llvm::isInlineViable(Function &F) {
           cast<CallInst>(Call)->canReturnTwice())
         return InlineResult::failure("exposes returns-twice attribute");
 
-      if (Call->getCalledFunction())
-        switch (Call->getCalledFunction()->getIntrinsicID()) {
+      if (Callee)
+        switch (Callee->getIntrinsicID()) {
         default:
           break;
         case llvm::Intrinsic::icall_branch_funnel:

diff  --git a/llvm/lib/CodeGen/StackProtector.cpp b/llvm/lib/CodeGen/StackProtector.cpp
index 8074793bc627..9e1a29e61a12 100644
--- a/llvm/lib/CodeGen/StackProtector.cpp
+++ b/llvm/lib/CodeGen/StackProtector.cpp
@@ -274,7 +274,6 @@ static const CallInst *findStackProtectorIntrinsic(Function &F) {
 bool StackProtector::RequiresStackProtector() {
   bool Strong = false;
   bool NeedsProtector = false;
-  HasPrologue = findStackProtectorIntrinsic(*F);
 
   if (F->hasFnAttribute(Attribute::SafeStack))
     return false;
@@ -295,8 +294,6 @@ bool StackProtector::RequiresStackProtector() {
     Strong = true; // Use the same heuristic as strong to determine SSPLayout
   } else if (F->hasFnAttribute(Attribute::StackProtectStrong))
     Strong = true;
-  else if (HasPrologue)
-    NeedsProtector = true;
   else if (!F->hasFnAttribute(Attribute::StackProtect))
     return false;
 

diff  --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index 63384689074d..82350ad6d29b 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -1939,6 +1939,16 @@ static void setOR(Function &Caller, const Function &Callee) {
 /// 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) {
+#ifndef NDEBUG
+  if (!Callee.hasFnAttribute(Attribute::AlwaysInline)) {
+    assert(!(!Callee.hasStackProtectorFnAttr() &&
+             Caller.hasStackProtectorFnAttr()) &&
+           "stack protected caller but callee requested no stack protector");
+    assert(!(!Caller.hasStackProtectorFnAttr() &&
+             Callee.hasStackProtectorFnAttr()) &&
+           "stack protected callee but caller requested no stack protector");
+  }
+#endif
   // 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/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index a316c7c9c068..a28577444407 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -610,6 +610,12 @@ void Function::clearGC() {
   setValueSubclassDataBit(14, false);
 }
 
+bool Function::hasStackProtectorFnAttr() const {
+  return hasFnAttribute(Attribute::StackProtect) ||
+         hasFnAttribute(Attribute::StackProtectStrong) ||
+         hasFnAttribute(Attribute::StackProtectReq);
+}
+
 /// Copy all additional attributes (those not needed to create a Function) from
 /// the Function Src to this one.
 void Function::copyAttributesFrom(const Function *Src) {

diff  --git a/llvm/test/CodeGen/AArch64/stack-guard-remat-bitcast.ll b/llvm/test/CodeGen/AArch64/stack-guard-remat-bitcast.ll
index 946d1cc31716..c4f3059ba287 100644
--- a/llvm/test/CodeGen/AArch64/stack-guard-remat-bitcast.ll
+++ b/llvm/test/CodeGen/AArch64/stack-guard-remat-bitcast.ll
@@ -1,22 +1,53 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
 ; RUN: llc < %s -mtriple=arm64-apple-ios -relocation-model=pic -frame-pointer=all | FileCheck %s
 
 @__stack_chk_guard = external global i64*
 
 ; PR20558
 
-; CHECK: adrp [[R0:x[0-9]+]], ___stack_chk_guard at GOTPAGE
-; CHECK: ldr  [[R1:x[0-9]+]], {{\[}}[[R0]], ___stack_chk_guard at GOTPAGEOFF{{\]}}
+define i32 @test_stack_guard_remat2() ssp {
+; CHECK-LABEL: test_stack_guard_remat2:
+; CHECK:       ; %bb.0: ; %entry
+; CHECK-NEXT:    sub sp, sp, #64 ; =64
+; CHECK-NEXT:    stp x29, x30, [sp, #48] ; 16-byte Folded Spill
+; CHECK-NEXT:    add x29, sp, #48 ; =48
+; CHECK-NEXT:    .cfi_def_cfa w29, 16
+; CHECK-NEXT:    .cfi_offset w30, -8
+; CHECK-NEXT:    .cfi_offset w29, -16
+; CHECK-NEXT:  Lloh0:
+; CHECK-NEXT:    adrp x8, ___stack_chk_guard at GOTPAGE
+; CHECK-NEXT:  Lloh1:
 ; Load the stack guard for the second time, just in case the previous value gets spilled.
-; CHECK: adrp [[GUARD_PAGE:x[0-9]+]], ___stack_chk_guard at GOTPAGE
-; CHECK: ldr  [[R2:x[0-9]+]], {{\[}}[[R1]]{{\]}}
-; CHECK: stur [[R2]], {{\[}}x29, [[SLOT0:[0-9#\-]+]]{{\]}}
-; CHECK: ldur [[R3:x[0-9]+]], {{\[}}x29, [[SLOT0]]{{\]}}
-; CHECK: ldr  [[GUARD_ADDR:x[0-9]+]], {{\[}}[[GUARD_PAGE]], ___stack_chk_guard at GOTPAGEOFF{{\]}}
-; CHECK: ldr  [[GUARD:x[0-9]+]], {{\[}}[[GUARD_ADDR]]{{\]}}
-; CHECK: cmp  [[GUARD]], [[R3]]
-; CHECK: b.ne LBB
-
-define i32 @test_stack_guard_remat2() {
+; CHECK-NEXT:    adrp x9, ___stack_chk_guard at GOTPAGE
+; CHECK-NEXT:  Lloh2:
+; CHECK-NEXT:    ldr x8, [x8, ___stack_chk_guard at GOTPAGEOFF]
+; CHECK-NEXT:  Lloh3:
+; CHECK-NEXT:    ldr x9, [x9, ___stack_chk_guard at GOTPAGEOFF]
+; CHECK-NEXT:  Lloh4:
+; CHECK-NEXT:    ldr x8, [x8]
+; CHECK-NEXT:  Lloh5:
+; CHECK-NEXT:    ldr x9, [x9]
+; CHECK-NEXT:    str x8, [sp]
+; CHECK-NEXT:    stur x9, [x29, #-8]
+; CHECK-NEXT:  Lloh6:
+; CHECK-NEXT:    adrp x9, ___stack_chk_guard at GOTPAGE
+; CHECK-NEXT:    ldur x8, [x29, #-8]
+; CHECK-NEXT:  Lloh7:
+; CHECK-NEXT:    ldr x9, [x9, ___stack_chk_guard at GOTPAGEOFF]
+; CHECK-NEXT:  Lloh8:
+; CHECK-NEXT:    ldr x9, [x9]
+; CHECK-NEXT:    cmp x9, x8
+; CHECK-NEXT:    b.ne LBB0_2
+; CHECK-NEXT:  ; %bb.1: ; %entry
+; CHECK-NEXT:    ldp x29, x30, [sp, #48] ; 16-byte Folded Reload
+; CHECK-NEXT:    mov w0, #-1
+; CHECK-NEXT:    add sp, sp, #64 ; =64
+; CHECK-NEXT:    ret
+; CHECK-NEXT:  LBB0_2: ; %entry
+; CHECK-NEXT:    bl ___stack_chk_fail
+; CHECK-NEXT:    .loh AdrpLdrGotLdr Lloh6, Lloh7, Lloh8
+; CHECK-NEXT:    .loh AdrpLdrGotLdr Lloh1, Lloh3, Lloh5
+; CHECK-NEXT:    .loh AdrpLdrGotLdr Lloh0, Lloh2, Lloh4
 entry:
   %StackGuardSlot = alloca i8*
   %StackGuard = load i8*, i8** bitcast (i64** @__stack_chk_guard to i8**)
@@ -26,5 +57,5 @@ entry:
   ret i32 -1
 }
 
-declare void @llvm.stackprotector(i8*, i8**)
-declare void @llvm.stackprotectorcheck(i8**)
+declare void @llvm.stackprotector(i8*, i8**) ssp
+declare void @llvm.stackprotectorcheck(i8**) ssp

diff  --git a/llvm/test/CodeGen/X86/stack-protector-2.ll b/llvm/test/CodeGen/X86/stack-protector-2.ll
index dc86b93cdcca..c6971a59f813 100644
--- a/llvm/test/CodeGen/X86/stack-protector-2.ll
+++ b/llvm/test/CodeGen/X86/stack-protector-2.ll
@@ -162,4 +162,34 @@ entry:
 
 declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg)
 
+; Intentionally does not have any fn attrs.
+declare dso_local void @foo(i8*)
+
+; @bar_sspstrong and @bar_nossp are the same function, but 
diff er only in
+; function attributes. Test that a callee without stack protector function
+; attribute does not trigger a stack guard slot in a caller that also does not
+; have a stack protector slot.
+define dso_local void @bar_sspstrong(i64 %0) #0 {
+; CHECK-LABEL: @bar_sspstrong
+; CHECK-NEXT: %StackGuardSlot = alloca i8*
+  %2 = alloca i64, align 8
+  store i64 %0, i64* %2, align 8
+  %3 = load i64, i64* %2, align 8
+  %4 = alloca i8, i64 %3, align 16
+  call void @foo(i8* %4)
+  ret void
+}
+
+; Intentionally does not have any fn attrs.
+define dso_local void @bar_nossp(i64 %0) {
+; CHECK-LABEL: @bar_nossp
+; CHECK-NEXT: %2 = alloca i64
+  %2 = alloca i64, align 8
+  store i64 %0, i64* %2, align 8
+  %3 = load i64, i64* %2, align 8
+  %4 = alloca i8, i64 %3, align 16
+  call void @foo(i8* %4)
+  ret void
+}
+
 attributes #0 = { sspstrong }

diff  --git a/llvm/test/ThinLTO/X86/nossp.ll b/llvm/test/ThinLTO/X86/nossp.ll
new file mode 100644
index 000000000000..128796c384dd
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/nossp.ll
@@ -0,0 +1,70 @@
+; RUN: split-file %s %t
+; RUN: opt -module-summary %t/a.ll -o %a.bc
+; RUN: opt -module-summary %t/b.ll -o %b.bc
+; RUN: llvm-lto2 run %a.bc %b.bc -o %c.bc -save-temps \
+; RUN:   -r=%a.bc,nossp_caller,px \
+; RUN:   -r=%a.bc,ssp_caller,px \
+; RUN:   -r=%a.bc,nossp_caller2,px \
+; RUN:   -r=%a.bc,ssp_caller2,px \
+; RUN:   -r=%a.bc,nossp_callee,x \
+; RUN:   -r=%a.bc,ssp_callee,x \
+; RUN:   -r=%b.bc,nossp_callee,px \
+; RUN:   -r=%b.bc,ssp_callee,px \
+; RUN:   -r=%b.bc,foo
+; RUN: llvm-dis %c.bc.1.4.opt.bc -o - | FileCheck %s
+
+;--- a.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"
+
+declare void @nossp_callee()
+declare void @ssp_callee() ssp
+
+; nossp caller should be able to inline nossp callee.
+define void @nossp_caller() {
+; CHECK-LABEL: @nossp_caller
+; CHECK-NEXT: tail call void @foo
+  tail call void @nossp_callee()
+  ret void
+}
+
+; ssp caller should be able to inline ssp callee.
+define void @ssp_caller() ssp {
+; CHECK-LABEL: @ssp_caller
+; CHECK-NEXT: tail call void @foo
+  tail call void @ssp_callee()
+  ret void
+}
+
+; nossp caller should *NOT* be able to inline ssp callee.
+define void @nossp_caller2() {
+; CHECK-LABEL: @nossp_caller2
+; CHECK-NEXT: tail call void @ssp_callee
+  tail call void @ssp_callee()
+  ret void
+}
+
+; ssp caller should *NOT* be able to inline nossp callee.
+define void @ssp_caller2() ssp {
+; CHECK-LABEL: @ssp_caller2
+; CHECK-NEXT: tail call void @nossp_callee
+  tail call void @nossp_callee()
+  ret void
+}
+
+;--- 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"
+
+declare void @foo()
+
+define void @nossp_callee() {
+  call void @foo()
+  ret void
+}
+
+define void @ssp_callee() ssp {
+  call void @foo()
+  ret void
+}

diff  --git a/llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll b/llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll
index 18c934bc6a1a..8725ee69c940 100644
--- a/llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll
+++ b/llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll
@@ -37,7 +37,7 @@ if.end:
   ret i32 %add
 }
 
-define i32 @callee_writeonly(i32 %v) writeonly {
+define i32 @callee_writeonly(i32 %v) writeonly ssp {
 entry:
   %cmp = icmp sgt i32 %v, 2000
   br i1 %cmp, label %if.then, label %if.end
@@ -58,7 +58,7 @@ if.end:
 ; CHECK: call void @callee_most.2.if.then(i32 %v
 ; CHECK: call i32 @callee_noinline(i32 %v)
 ; CHECK: call void @callee_writeonly.1.if.then(i32 %v
-define i32 @caller(i32 %v) {
+define i32 @caller(i32 %v) ssp {
 entry:
   %c1 = call i32 @callee_most(i32 %v)
   %c2 = call i32 @callee_noinline(i32 %v)
@@ -66,7 +66,7 @@ entry:
   ret i32 %c3
 }
 
-; CHECK: define internal void @callee_writeonly.1.if.then(i32 %v, i32* %sub.out) { 
+; CHECK: define internal void @callee_writeonly.1.if.then(i32 %v, i32* %sub.out) [[FN_ATTRS0:#[0-9]+]]
 ; CHECK: define internal void @callee_most.2.if.then(i32 %v, i32* %sub.out)  [[FN_ATTRS:#[0-9]+]]
 
 ; attributes to preserve
@@ -76,6 +76,7 @@ attributes #0 = {
   sanitize_thread ssp sspreq sspstrong strictfp uwtable "foo"="bar"
   "patchable-function"="prologue-short-redirect" "probe-stack"="_foo_guard" "stack-probe-size"="4096" }
 
+; CHECK: attributes [[FN_ATTRS0]] = { ssp
 ; CHECK: attributes [[FN_ATTRS]] = { inlinehint minsize noduplicate noimplicitfloat norecurse noredzone nounwind nonlazybind optsize safestack sanitize_address sanitize_hwaddress sanitize_memory sanitize_thread ssp sspreq sspstrong strictfp uwtable "foo"="bar" "patchable-function"="prologue-short-redirect" "probe-stack"="_foo_guard" "stack-probe-size"="4096" }
 
 ; attributes to drop

diff  --git a/llvm/test/Transforms/Inline/devirtualize.ll b/llvm/test/Transforms/Inline/devirtualize.ll
index eaba1a8c19c5..cdeae519d0f0 100644
--- a/llvm/test/Transforms/Inline/devirtualize.ll
+++ b/llvm/test/Transforms/Inline/devirtualize.ll
@@ -98,7 +98,7 @@ entry:
   ret i32 4
 }
 
-define linkonce_odr i32 @_ZThn8_N1D1fEv(%struct.C* %this) {
+define linkonce_odr i32 @_ZThn8_N1D1fEv(%struct.C* %this) ssp {
 entry:
   %0 = bitcast %struct.C* %this to i8*            ; <i8*> [#uses=1]
   %1 = getelementptr inbounds i8, i8* %0, i64 -8      ; <i8*> [#uses=1]

diff  --git a/llvm/test/Transforms/Inline/inline-byval-bonus.ll b/llvm/test/Transforms/Inline/inline-byval-bonus.ll
index ec4610ed9a77..255d1c6e2c37 100644
--- a/llvm/test/Transforms/Inline/inline-byval-bonus.ll
+++ b/llvm/test/Transforms/Inline/inline-byval-bonus.ll
@@ -15,7 +15,7 @@ target triple = "x86_64-apple-macosx10.8.0"
 %struct.ray = type { %struct.vec3, %struct.vec3 }
 %struct.spoint = type { %struct.vec3, %struct.vec3, %struct.vec3, double }
 
-define i32 @caller(%struct.sphere* %i) {
+define i32 @caller(%struct.sphere* %i) ssp {
   %shadow_ray = alloca %struct.ray, align 8
   call void @fix(%struct.ray* %shadow_ray)
 

diff  --git a/llvm/test/Transforms/Inline/inline_nossp.ll b/llvm/test/Transforms/Inline/inline_nossp.ll
new file mode 100644
index 000000000000..dde75cc52434
--- /dev/null
+++ b/llvm/test/Transforms/Inline/inline_nossp.ll
@@ -0,0 +1,97 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -inline -o - -S %s | FileCheck %s
+; RUN: opt -passes='cgscc(inline)' %s -S | FileCheck %s
+; RUN: opt -always-inline -o - -S %s | FileCheck %s
+; RUN: opt -passes=always-inline -o - -S %s | FileCheck %s
+
+declare dso_local void @foo(i8*)
+
+; Not interesting to test.
+define dso_local void @ssp(i64 %0) #0 {
+  %2 = alloca i64, align 8
+  store i64 %0, i64* %2, align 8
+  %3 = load i64, i64* %2, align 8
+  %4 = alloca i8, i64 %3, align 16
+  call void @foo(i8* %4)
+  ret void
+}
+
+; Not interesting to test.
+define dso_local void @ssp_alwaysinline(i64 %0) #1 {
+  %2 = alloca i64, align 8
+  store i64 %0, i64* %2, align 8
+  %3 = load i64, i64* %2, align 8
+  %4 = alloca i8, i64 %3, align 16
+  call void @foo(i8* %4)
+  ret void
+}
+
+; @ssp should not be inlined due to mismatch stack protector.
+; @ssp_alwaysinline should be inlined due to alwaysinline.
+define dso_local void @nossp() {
+; CHECK-LABEL: @nossp(
+; CHECK-NEXT:    [[TMP1:%.*]] = alloca i64, align 8
+; CHECK-NEXT:    call void @ssp(i64 1024)
+; CHECK-NEXT:    [[SAVEDSTACK:%.*]] = call i8* @llvm.stacksave()
+; CHECK-NEXT:    [[TMP2:%.*]] = bitcast i64* [[TMP1]] to i8*
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0i8(i64 8, i8* [[TMP2]])
+; CHECK-NEXT:    store i64 1024, i64* [[TMP1]], align 8
+; CHECK-NEXT:    [[TMP3:%.*]] = load i64, i64* [[TMP1]], align 8
+; CHECK-NEXT:    [[TMP4:%.*]] = alloca i8, i64 [[TMP3]], align 16
+; CHECK-NEXT:    call void @foo(i8* [[TMP4]])
+; CHECK-NEXT:    [[TMP5:%.*]] = bitcast i64* [[TMP1]] to i8*
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0i8(i64 8, i8* [[TMP5]])
+; CHECK-NEXT:    call void @llvm.stackrestore(i8* [[SAVEDSTACK]])
+; CHECK-NEXT:    ret void
+;
+  call void @ssp(i64 1024)
+  call void @ssp_alwaysinline(i64 1024)
+  ret void
+}
+
+; This is the same case as @nossp above. That the caller has alwaysinline is
+; irrelevant.  Not interesting to test.
+define dso_local void @nossp_alwaysinline() #2 {
+  call void @ssp(i64 1024)
+  call void @ssp_alwaysinline(i64 1024)
+  ret void
+}
+
+; @nossp_alwaysinline should be inlined due to alwaysinline.
+; @ssp should not be inlined due to mismatch stack protector.
+; @ssp_alwaysinline should be inlined due to alwaysinline.
+define dso_local void @nossp_caller() {
+; CHECK-LABEL: @nossp_caller(
+; CHECK-NEXT:    [[TMP1:%.*]] = alloca i64, align 8
+; CHECK-NEXT:    [[SAVEDSTACK:%.*]] = call i8* @llvm.stacksave()
+; CHECK-NEXT:    call void @ssp(i64 1024)
+; CHECK-NEXT:    [[SAVEDSTACK_I:%.*]] = call i8* @llvm.stacksave()
+; CHECK-NEXT:    [[TMP2:%.*]] = bitcast i64* [[TMP1]] to i8*
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0i8(i64 8, i8* [[TMP2]])
+; CHECK-NEXT:    store i64 1024, i64* [[TMP1]], align 8
+; CHECK-NEXT:    [[TMP3:%.*]] = load i64, i64* [[TMP1]], align 8
+; CHECK-NEXT:    [[TMP4:%.*]] = alloca i8, i64 [[TMP3]], align 16
+; CHECK-NEXT:    call void @foo(i8* [[TMP4]])
+; CHECK-NEXT:    [[TMP5:%.*]] = bitcast i64* [[TMP1]] to i8*
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0i8(i64 8, i8* [[TMP5]])
+; CHECK-NEXT:    call void @llvm.stackrestore(i8* [[SAVEDSTACK_I]])
+; CHECK-NEXT:    call void @llvm.stackrestore(i8* [[SAVEDSTACK]])
+; CHECK-NEXT:    ret void
+;
+  call void @nossp_alwaysinline()
+  ret void
+}
+
+; @nossp should not be inlined due to mismatch stack protector.
+define dso_local void @ssp2() #0 {
+; CHECK-LABEL: @ssp2(
+; CHECK-NEXT:    call void @nossp()
+; CHECK-NEXT:    ret void
+;
+  call void @nossp()
+  ret void
+}
+
+attributes #0 = { sspstrong }
+attributes #1 = { sspstrong alwaysinline }
+attributes #2 = { alwaysinline}

diff  --git a/llvm/test/Transforms/Inline/inline_ssp.ll b/llvm/test/Transforms/Inline/inline_ssp.ll
index bad332dbff0a..2bf93d322842 100644
--- a/llvm/test/Transforms/Inline/inline_ssp.ll
+++ b/llvm/test/Transforms/Inline/inline_ssp.ll
@@ -61,7 +61,7 @@ entry:
 
 define void @inline_req_nossp() nounwind uwtable {
 entry:
-; CHECK: @inline_req_nossp() #0
+; CHECK: @inline_req_nossp() #3
   call void @fun_sspreq()
   ret void
 }
@@ -90,7 +90,7 @@ entry:
 
 define void @inline_strong_nossp() nounwind uwtable {
 entry:
-; CHECK: @inline_strong_nossp() #1
+; CHECK: @inline_strong_nossp() #3
   call void @fun_sspstrong()
   ret void
 }
@@ -119,7 +119,7 @@ entry:
 
 define void @inline_ssp_nossp() nounwind uwtable {
 entry:
-; CHECK: @inline_ssp_nossp() #2
+; CHECK: @inline_ssp_nossp() #3
   call void @fun_ssp()
   ret void
 }


        


More information about the llvm-commits mailing list