[llvm] 57335b6 - [stack-safety] Allow to determine safe accesses.

Florian Mayer via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 10 11:24:16 PDT 2021


Author: Florian Mayer
Date: 2021-09-10T19:23:54+01:00
New Revision: 57335b6e2eaddfbef5d68b022fb1a65199c1cd86

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

LOG: [stack-safety] Allow to determine safe accesses.

Reviewed By: vitalybuka

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

Added: 
    

Modified: 
    llvm/include/llvm/Analysis/StackSafetyAnalysis.h
    llvm/lib/Analysis/StackSafetyAnalysis.cpp
    llvm/test/Analysis/StackSafetyAnalysis/ipa-alias.ll
    llvm/test/Analysis/StackSafetyAnalysis/ipa.ll
    llvm/test/Analysis/StackSafetyAnalysis/local.ll
    llvm/test/Analysis/StackSafetyAnalysis/memintrin.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Analysis/StackSafetyAnalysis.h b/llvm/include/llvm/Analysis/StackSafetyAnalysis.h
index 59c1e3e3bd560..a028107e213ce 100644
--- a/llvm/include/llvm/Analysis/StackSafetyAnalysis.h
+++ b/llvm/include/llvm/Analysis/StackSafetyAnalysis.h
@@ -75,7 +75,12 @@ class StackSafetyGlobalInfo {
   StackSafetyGlobalInfo &operator=(StackSafetyGlobalInfo &&);
   ~StackSafetyGlobalInfo();
 
+  // Whether we can prove that all accesses to this Alloca are in-range and
+  // during its lifetime.
   bool isSafe(const AllocaInst &AI) const;
+  // Whether we can prove that an instruction only accesses a live alloca in
+  // range.
+  bool accessIsSafe(const Instruction &I) const;
   void print(raw_ostream &O) const;
   void dump() const;
 };

diff  --git a/llvm/lib/Analysis/StackSafetyAnalysis.cpp b/llvm/lib/Analysis/StackSafetyAnalysis.cpp
index 628fc0647b29e..18e303e55a599 100644
--- a/llvm/lib/Analysis/StackSafetyAnalysis.cpp
+++ b/llvm/lib/Analysis/StackSafetyAnalysis.cpp
@@ -30,6 +30,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <memory>
+#include <tuple>
 
 using namespace llvm;
 
@@ -116,6 +117,7 @@ template <typename CalleeTy> struct UseInfo {
   // Access range if the address (alloca or parameters).
   // It is allowed to be empty-set when there are no known accesses.
   ConstantRange Range;
+  std::map<const Instruction *, ConstantRange> Accesses;
 
   // List of calls which pass address as an argument.
   // Value is offset range of address from base address (alloca or calling
@@ -130,6 +132,9 @@ template <typename CalleeTy> struct UseInfo {
 
   void updateRange(const ConstantRange &R) { Range = unionNoWrap(Range, R); }
   void addRange(const Instruction *I, const ConstantRange &R) {
+    auto Ins = Accesses.emplace(I, R);
+    if (!Ins.second)
+      Ins.first->second = unionNoWrap(Ins.first->second, R);
     updateRange(R);
   }
 };
@@ -225,6 +230,7 @@ struct StackSafetyInfo::InfoTy {
 struct StackSafetyGlobalInfo::InfoTy {
   GVToSSI Info;
   SmallPtrSet<const AllocaInst *, 8> SafeAllocas;
+  SmallPtrSet<const Instruction *, 8> SafeAccesses;
 };
 
 namespace {
@@ -351,7 +357,7 @@ void StackSafetyLocalAnalysis::analyzeAllUses(Value *Ptr,
       case Instruction::Load: {
         if (AI && !SL.isAliveAfter(AI, I)) {
           US.addRange(I, UnknownRange);
-          return;
+          break;
         }
         US.addRange(I,
                     getAccessRange(UI, Ptr, DL.getTypeStoreSize(I->getType())));
@@ -365,11 +371,11 @@ void StackSafetyLocalAnalysis::analyzeAllUses(Value *Ptr,
         if (V == I->getOperand(0)) {
           // Stored the pointer - conservatively assume it may be unsafe.
           US.addRange(I, UnknownRange);
-          return;
+          break;
         }
         if (AI && !SL.isAliveAfter(AI, I)) {
           US.addRange(I, UnknownRange);
-          return;
+          break;
         }
         US.addRange(
             I, getAccessRange(
@@ -382,7 +388,7 @@ void StackSafetyLocalAnalysis::analyzeAllUses(Value *Ptr,
         // FIXME: Process parameters correctly. This is a leak only if we return
         // alloca.
         US.addRange(I, UnknownRange);
-        return;
+        break;
 
       case Instruction::Call:
       case Instruction::Invoke: {
@@ -391,7 +397,7 @@ void StackSafetyLocalAnalysis::analyzeAllUses(Value *Ptr,
 
         if (AI && !SL.isAliveAfter(AI, I)) {
           US.addRange(I, UnknownRange);
-          return;
+          break;
         }
 
         if (const MemIntrinsic *MI = dyn_cast<MemIntrinsic>(I)) {
@@ -402,7 +408,7 @@ void StackSafetyLocalAnalysis::analyzeAllUses(Value *Ptr,
         const auto &CB = cast<CallBase>(*I);
         if (!CB.isArgOperand(&UI)) {
           US.addRange(I, UnknownRange);
-          return;
+          break;
         }
 
         unsigned ArgNo = CB.getArgOperandNo(&UI);
@@ -420,7 +426,7 @@ void StackSafetyLocalAnalysis::analyzeAllUses(Value *Ptr,
             dyn_cast<GlobalValue>(CB.getCalledOperand()->stripPointerCasts());
         if (!Callee) {
           US.addRange(I, UnknownRange);
-          return;
+          break;
         }
 
         assert(isa<Function>(Callee) || isa<GlobalAlias>(Callee));
@@ -808,17 +814,27 @@ const StackSafetyGlobalInfo::InfoTy &StackSafetyGlobalInfo::getInfo() const {
       }
     }
     Info.reset(new InfoTy{
-        createGlobalStackSafetyInfo(std::move(Functions), Index), {}});
+        createGlobalStackSafetyInfo(std::move(Functions), Index), {}, {}});
+
+    std::map<const Instruction *, bool> AccessIsUnsafe;
     for (auto &FnKV : Info->Info) {
       for (auto &KV : FnKV.second.Allocas) {
         ++NumAllocaTotal;
         const AllocaInst *AI = KV.first;
-        if (getStaticAllocaSizeRange(*AI).contains(KV.second.Range)) {
+        auto AIRange = getStaticAllocaSizeRange(*AI);
+        if (AIRange.contains(KV.second.Range)) {
           Info->SafeAllocas.insert(AI);
           ++NumAllocaStackSafe;
         }
+        for (const auto &A : KV.second.Accesses)
+          AccessIsUnsafe[A.first] |= !AIRange.contains(A.second);
       }
     }
+
+    for (const auto &KV : AccessIsUnsafe)
+      if (!KV.second)
+        Info->SafeAccesses.insert(KV.first);
+
     if (StackSafetyPrint)
       print(errs());
   }
@@ -888,6 +904,11 @@ bool StackSafetyGlobalInfo::isSafe(const AllocaInst &AI) const {
   return Info.SafeAllocas.count(&AI);
 }
 
+bool StackSafetyGlobalInfo::accessIsSafe(const Instruction &I) const {
+  const auto &Info = getInfo();
+  return Info.SafeAccesses.count(&I);
+}
+
 void StackSafetyGlobalInfo::print(raw_ostream &O) const {
   auto &SSI = getInfo().Info;
   if (SSI.empty())
@@ -896,7 +917,13 @@ void StackSafetyGlobalInfo::print(raw_ostream &O) const {
   for (auto &F : M.functions()) {
     if (!F.isDeclaration()) {
       SSI.find(&F)->second.print(O, F.getName(), &F);
-      O << "\n";
+      O << "    safe accesses:"
+        << "\n";
+      for (const auto &I : instructions(F)) {
+        if (accessIsSafe(I)) {
+          O << "     " << I << "\n";
+        }
+      }
       O << "\n";
     }
   }

diff  --git a/llvm/test/Analysis/StackSafetyAnalysis/ipa-alias.ll b/llvm/test/Analysis/StackSafetyAnalysis/ipa-alias.ll
index 02f3b257bada2..2bce5b9716e2b 100644
--- a/llvm/test/Analysis/StackSafetyAnalysis/ipa-alias.ll
+++ b/llvm/test/Analysis/StackSafetyAnalysis/ipa-alias.ll
@@ -66,6 +66,7 @@ define void @PreemptableAliasCall() #0 {
 ; GLOBAL-NEXT: x1[1]: full-set, @PreemptableAliasWrite1(arg0, [0,1)){{$}}
 ; LOCAL-NEXT: x2[1]: empty-set, @AliasToPreemptableAliasWrite1(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: x2[1]: [0,1), @AliasToPreemptableAliasWrite1(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x1 = alloca i8
@@ -85,6 +86,7 @@ define void @InterposableAliasCall() #0 {
 ; LOCAL-NEXT: x[1]: empty-set, @InterposableAliasWrite1(arg0, [0,1)){{$}}
 ; NOLTO-NEXT: x[1]: full-set, @InterposableAliasWrite1(arg0, [0,1)){{$}}
 ; LTO-NEXT: x[1]: [0,1), @InterposableAliasWrite1(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i8
@@ -100,6 +102,7 @@ define void @AliasCall() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[1]: empty-set, @AliasWrite1(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: x[1]: [0,1), @AliasWrite1(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i8
@@ -116,6 +119,7 @@ define void @BitcastAliasCall() #0 {
 ; GLOBAL-NEXT: x1[4]: [0,1), @BitcastAliasWrite1(arg0, [0,1)){{$}}
 ; LOCAL-NEXT: x2[1]: empty-set, @AliasToBitcastAliasWrite1(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: x2[1]: [0,1), @AliasToBitcastAliasWrite1(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x1 = alloca i32
@@ -131,4 +135,5 @@ entry:
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: p[]: [0,1){{$}}
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:

diff  --git a/llvm/test/Analysis/StackSafetyAnalysis/ipa.ll b/llvm/test/Analysis/StackSafetyAnalysis/ipa.ll
index fb63d53ae22f2..69ec4dac88c99 100644
--- a/llvm/test/Analysis/StackSafetyAnalysis/ipa.ll
+++ b/llvm/test/Analysis/StackSafetyAnalysis/ipa.ll
@@ -126,6 +126,7 @@ define void @f1() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[4]: empty-set, @Write8(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: x[4]: [0,8), @Write8(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -141,6 +142,7 @@ define void @f2() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[4]: empty-set, @Write1(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: x[4]: [0,1), @Write1(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -156,6 +158,7 @@ define void @f3() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[4]: empty-set, @Write4(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: x[4]: [0,4), @Write4(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -171,6 +174,7 @@ define void @f4() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[4]: empty-set, @Write1(arg0, [1,2)){{$}}
 ; GLOBAL-NEXT: x[4]: [1,2), @Write1(arg0, [1,2)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -187,6 +191,7 @@ define void @f5() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: empty-set, @Write4(arg0, [1,2)){{$}}
 ; GLOBAL-NEXT: [1,5), @Write4(arg0, [1,2)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -203,6 +208,7 @@ define void @f6() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[4]: empty-set, @ExternalCall(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: x[4]: full-set, @ExternalCall(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -218,6 +224,7 @@ define void @PreemptableCall() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[4]: empty-set, @PreemptableWrite1(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: x[4]: full-set, @PreemptableWrite1(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -234,6 +241,7 @@ define void @InterposableCall() #0 {
 ; LOCAL-NEXT: x[4]: empty-set, @InterposableWrite1(arg0, [0,1)){{$}}
 ; NOLTO-NEXT: x[4]: full-set, @InterposableWrite1(arg0, [0,1)){{$}}
 ; LTO-NEXT: x[4]: [0,1), @InterposableWrite1(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -249,6 +257,7 @@ define void @PrivateCall() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[4]: empty-set, @PrivateWrite1(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: x[4]: [0,1), @PrivateWrite1(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -262,6 +271,7 @@ define private void @PrivateWrite1(i8* %p) #0 {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: p[]: [0,1){{$}}
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   store i8 0, i8* %p, align 1
@@ -276,6 +286,7 @@ define void @f7() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[4]: empty-set, @ReturnDependent(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: x[4]: full-set, @ReturnDependent(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -290,6 +301,7 @@ define void @f8left() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[8]: empty-set, @Rec2(arg0, [2,3)){{$}}
 ; GLOBAL-NEXT: x[8]: [0,4), @Rec2(arg0, [2,3)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -306,6 +318,7 @@ define void @f8right() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[8]: empty-set, @Rec2(arg0, [6,7)){{$}}
 ; GLOBAL-NEXT: x[8]: [4,8), @Rec2(arg0, [6,7)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -322,6 +335,7 @@ define void @f8oobleft() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[8]: empty-set, @Rec2(arg0, [1,2)){{$}}
 ; GLOBAL-NEXT: x[8]: [-1,3), @Rec2(arg0, [1,2)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -338,6 +352,7 @@ define void @f8oobright() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[8]: empty-set, @Rec2(arg0, [7,8)){{$}}
 ; GLOBAL-NEXT: x[8]: [5,9), @Rec2(arg0, [7,8)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -354,6 +369,7 @@ define void @TwoArguments() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[8]: empty-set, @Write4_2(arg0, [4,5)), @Write4_2(arg1, [0,1)){{$}}
 ; GLOBAL-NEXT: x[8]: [0,8), @Write4_2(arg0, [4,5)), @Write4_2(arg1, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -369,6 +385,7 @@ define void @TwoArgumentsOOBOne() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[8]: empty-set, @Write4_2(arg0, [5,6)), @Write4_2(arg1, [0,1)){{$}}
 ; GLOBAL-NEXT: x[8]: [0,9), @Write4_2(arg0, [5,6)), @Write4_2(arg1, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -384,6 +401,7 @@ define void @TwoArgumentsOOBOther() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[8]: empty-set, @Write4_2(arg0, [4,5)), @Write4_2(arg1, [-1,0)){{$}}
 ; GLOBAL-NEXT: x[8]: [-1,8), @Write4_2(arg0, [4,5)), @Write4_2(arg1, [-1,0)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -400,6 +418,7 @@ define void @TwoArgumentsOOBBoth() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[8]: empty-set, @Write4_2(arg0, [5,6)), @Write4_2(arg1, [-1,0)){{$}}
 ; GLOBAL-NEXT: x[8]: [-1,9), @Write4_2(arg0, [5,6)), @Write4_2(arg1, [-1,0)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -417,6 +436,9 @@ define i32 @TestRecursiveNoOffset(i32* %p, i32 %size) #0 {
 ; GLOBAL-NEXT: p[]: full-set, @RecursiveNoOffset(arg0, [0,1)){{$}}
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: sum[4]: [0,4), @RecursiveNoOffset(arg2, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: store i32 0, i32* %sum, align 4
+; GLOBAL-NEXT: %1 = load i32, i32* %sum, align 4
 ; CHECK-EMPTY:
 entry:
   %sum = alloca i32, align 4
@@ -433,6 +455,7 @@ define void @TestRecursiveWithOffset(i32 %size) #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: sum[64]: empty-set, @RecursiveWithOffset(arg1, [0,1)){{$}}
 ; GLOBAL-NEXT: sum[64]: full-set, @RecursiveWithOffset(arg1, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %sum = alloca i32, i64 16, align 4
@@ -447,6 +470,7 @@ define void @TestUpdateArg() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[16]: empty-set, @WriteAndReturn8(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: x[16]: full-set, @WriteAndReturn8(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i8, i64 16, align 4
@@ -460,6 +484,7 @@ define void @TestCrossModuleOnce() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: y[1]: empty-set, @Write1SameModule(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: y[1]: [0,1), @Write1SameModule(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %y = alloca i8, align 4
@@ -473,6 +498,7 @@ define void @TestCrossModuleTwice() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: z[1]: empty-set, @Write1DiffModule(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: z[1]: [0,1), @Write1DiffModule(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %z = alloca i8, align 4
@@ -486,6 +512,7 @@ define void @TestCrossModuleConflict() #0 {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[1]: empty-set, @Write1Private(arg0, [0,1)){{$}}
 ; GLOBAL-NEXT: x[1]: [-1,0), @Write1Private(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i8, align 4
@@ -501,6 +528,7 @@ define void @TestCrossModuleWeak() #0 {
 ; LOCAL-NEXT: x[1]: empty-set, @Write1Weak(arg0, [0,1)){{$}}
 ; NOLTO-NEXT: x[1]: [1,2), @Write1Weak(arg0, [0,1)){{$}}
 ; LTO-NEXT: x[1]: full-set, @Write1Weak(arg0, [0,1)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i8, align 4
@@ -534,12 +562,14 @@ entry:
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: p[]: [0,1){{$}}
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @Write4{{$}}
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: p[]: [0,4){{$}}
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @Write4_2{{$}}
@@ -547,36 +577,42 @@ entry:
 ; CHECK-NEXT: p[]: [0,4){{$}}
 ; CHECK-NEXT: q[]: [0,4){{$}}
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @Write8{{$}}
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: p[]: [0,8){{$}}
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @WriteAndReturn8{{$}}
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: p[]: full-set{{$}}
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @PreemptableWrite1 dso_preemptable{{$}}
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: p[]: [0,1){{$}}
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @InterposableWrite1 interposable{{$}}
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: p[]: [0,1){{$}}
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @ReturnDependent{{$}}
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: p[]: full-set{{$}}
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @Rec0{{$}}
@@ -584,6 +620,7 @@ entry:
 ; LOCAL-NEXT: p[]: empty-set, @Write4(arg0, [2,3)){{$}}
 ; GLOBAL-NEXT: p[]: [2,6)
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @Rec1{{$}}
@@ -591,6 +628,7 @@ entry:
 ; LOCAL-NEXT: p[]: empty-set, @Rec0(arg0, [1,2)){{$}}
 ; GLOBAL-NEXT: p[]: [3,7)
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @Rec2{{$}}
@@ -598,6 +636,7 @@ entry:
 ; LOCAL-NEXT: p[]: empty-set, @Rec1(arg0, [-5,-4)){{$}}
 ; GLOBAL-NEXT: p[]: [-2,2)
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @RecursiveNoOffset{{$}}
@@ -606,6 +645,7 @@ entry:
 ; GLOBAL-NEXT: p[]: full-set, @RecursiveNoOffset(arg0, [4,5)){{$}}
 ; CHECK-NEXT: acc[]: [0,4), @RecursiveNoOffset(arg2, [0,1)){{$}}
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @RecursiveWithOffset{{$}}
@@ -613,12 +653,14 @@ entry:
 ; LOCAL-NEXT: acc[]: [0,4), @RecursiveWithOffset(arg1, [4,5)){{$}}
 ; GLOBAL-NEXT: acc[]: full-set, @RecursiveWithOffset(arg1, [4,5)){{$}}
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; CHECK-LABEL: @ReturnAlloca
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[8]: full-set
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 
 ; INDEX-LABEL: ^0 = module:

diff  --git a/llvm/test/Analysis/StackSafetyAnalysis/local.ll b/llvm/test/Analysis/StackSafetyAnalysis/local.ll
index 5fb59963609ec..6c0fbef8a2661 100644
--- a/llvm/test/Analysis/StackSafetyAnalysis/local.ll
+++ b/llvm/test/Analysis/StackSafetyAnalysis/local.ll
@@ -11,12 +11,15 @@ declare void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i1 %isvol
 declare void @llvm.memmove.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i1 %isvolatile)
 declare void @llvm.memset.p0i8.i64(i8* %dest, i8 %val, i64 %len, i1 %isvolatile)
 
+declare void @unknown_call(i8* %dest)
+
 ; Address leaked.
 define void @LeakAddress() {
 ; CHECK-LABEL: @LeakAddress dso_preemptable{{$}}
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: full-set{{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -30,6 +33,8 @@ define void @StoreInBounds() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,1){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: store i8 0, i8* %x1, align 1
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -43,6 +48,8 @@ define void @StoreInBounds2() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,4){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: store i32 0, i32* %x, align 4
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -55,6 +62,8 @@ define void @StoreInBounds3() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [2,3){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: store i8 0, i8* %x2, align 1
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -70,6 +79,7 @@ define void @StoreInBounds4() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: full-set{{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -85,6 +95,7 @@ define dso_local void @WriteMinMax(i8* %p) {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: p[]: full-set
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %p1 = getelementptr i8, i8* %p, i64 9223372036854775805
@@ -99,6 +110,7 @@ define dso_local void @WriteMax(i8* %p) {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: p[]: [-9223372036854775807,9223372036854775806)
 ; CHECK-NEXT: allocas uses:
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   call void @llvm.memset.p0i8.i64(i8* %p, i8 1, i64 9223372036854775806, i1 0)
@@ -112,6 +124,7 @@ define void @StoreOutOfBounds() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [2,6){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -128,6 +141,8 @@ define void @LoadInBounds() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,1){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: %v = load i8, i8* %x1, align 1
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -141,6 +156,7 @@ define void @LoadOutOfBounds() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [2,6){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -157,6 +173,7 @@ define i8* @Ret() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: full-set{{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -173,6 +190,7 @@ define void @DirectCall() {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[8]: empty-set, @Foo(arg0, [2,3)){{$}}
 ; GLOBAL-NEXT: x[8]: full-set, @Foo(arg0, [2,3)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -190,6 +208,7 @@ define void @IndirectCall(void (i8*)* %p) {
 ; CHECK-NEXT: p[]: full-set{{$}}
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: full-set{{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -204,6 +223,8 @@ define void @NonConstantOffset(i1 zeroext %z) {
 ; CHECK-NEXT: allocas uses:
 ; FIXME: SCEV can't look through selects.
 ; CHECK-NEXT: x[4]: [0,4){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: store i8 0, i8* %x2, align 1
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -219,6 +240,7 @@ define void @NegativeOffset() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[40]: [-1600000000000,-1599999999996){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, i32 10, align 4
@@ -232,6 +254,7 @@ define void @PossiblyNegativeOffset(i16 %z) {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[40]: [-131072,131072){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, i32 10, align 4
@@ -245,6 +268,7 @@ define void @NonConstantOffsetOOB(i1 zeroext %z) {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,6){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -260,6 +284,8 @@ define void @ArrayAlloca() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[40]: [36,40){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: store i32 0, i32* %x3, align 1
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, i32 10, align 4
@@ -275,6 +301,7 @@ define void @ArrayAllocaOOB() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[40]: [37,41){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, i32 10, align 4
@@ -290,6 +317,7 @@ define void @DynamicAllocaUnused(i64 %size) {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[0]: empty-set{{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, i64 %size, align 16
@@ -302,6 +330,7 @@ define void @DynamicAlloca(i64 %size) {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[0]: [0,4){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, i64 %size, align 16
@@ -316,6 +345,7 @@ define void @DynamicAllocaFiniteSizeRange(i1 zeroext %z) {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[0]: [0,4){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %size = select i1 %z, i64 3, i64 5
@@ -329,6 +359,8 @@ define signext i8 @SimpleLoop() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[10]: [0,10){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: %1 = load volatile i8, i8* %p.09, align 1
 ; CHECK-EMPTY:
 entry:
   %x = alloca [10 x i8], align 1
@@ -355,6 +387,7 @@ define signext i8 @SimpleLoopOOB() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[10]: [0,11){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca [10 x i8], align 1
@@ -381,6 +414,7 @@ define dso_local void @SizeCheck(i32 %sz) {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x1[128]: [0,4294967295){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x1 = alloca [128 x i8], align 16
@@ -405,6 +439,7 @@ define void @Scalable(<vscale x 4 x i32>* %p, <vscale x 4 x i32>* %unused, <vsca
 ; CHECK-NEXT:   unused[]: empty-set
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT:   x[0]: [0,1){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca <vscale x 4 x i32>, align 4
@@ -422,6 +457,8 @@ define void @ZeroSize(%zerosize_type *%p)  {
 ; CHECK-NEXT:   p[]: empty-set
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT:   x[0]: empty-set
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: store %zerosize_type undef, %zerosize_type* %x, align 4
 ; CHECK-EMPTY:
 entry:
   %x = alloca %zerosize_type, align 4
@@ -436,6 +473,7 @@ define void @OperandBundle() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT:   a[4]: full-set
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %a = alloca i32, align 4
@@ -447,6 +485,7 @@ define void @ByVal(i16* byval(i16) %p) {
   ; CHECK-LABEL: @ByVal dso_preemptable{{$}}
   ; CHECK-NEXT: args uses:
   ; CHECK-NEXT: allocas uses:
+  ; GLOBAL-NEXT: safe accesses:
   ; CHECK-EMPTY:
 entry:
   ret void
@@ -458,6 +497,9 @@ define void @TestByVal() {
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[2]: [0,2)
 ; CHECK-NEXT: y[8]: [0,2)
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: call void @ByVal(i16* byval(i16) %x)
+; GLOBAL-NEXT: call void @ByVal(i16* byval(i16) %y1)
 ; CHECK-EMPTY:
 entry:
   %x = alloca i16, align 4
@@ -477,6 +519,7 @@ define void @TestByValArray() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: z[800000]: [500000,1300000)
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %z = alloca [100000 x i64], align 4
@@ -492,6 +535,7 @@ define dso_local i8 @LoadMinInt64(i8* %p) {
   ; CHECK-NEXT: args uses:
   ; CHECK-NEXT: p[]: [-9223372036854775808,-9223372036854775807){{$}}
   ; CHECK-NEXT: allocas uses:
+  ; GLOBAL-NEXT: safe accesses:
   ; CHECK-EMPTY:
   %p2 = getelementptr i8, i8* %p, i64 -9223372036854775808
   %v = load i8, i8* %p2, align 1
@@ -504,6 +548,7 @@ define void @Overflow() {
 ; CHECK-NEXT: allocas uses:
 ; LOCAL-NEXT: x[1]: empty-set, @LoadMinInt64(arg0, [-9223372036854775808,-9223372036854775807)){{$}}
 ; GLOBAL-NEXT: x[1]: full-set, @LoadMinInt64(arg0, [-9223372036854775808,-9223372036854775807)){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i8, align 4
@@ -518,6 +563,7 @@ define void @DeadBlock(i64* %p) {
 ; CHECK-NEXT: p[]: empty-set{{$}}
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[1]: empty-set{{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i8, align 4
@@ -539,6 +585,7 @@ define void @LifeNotStarted() {
 ; CHECK: x[1]: full-set{{$}}
 ; CHECK: y[1]: full-set{{$}}
 ; CHECK: z[1]: full-set{{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i8, align 4
@@ -563,6 +610,10 @@ define void @LifeOK() {
 ; CHECK: x[1]: [0,1){{$}}
 ; CHECK: y[1]: [0,1){{$}}
 ; CHECK: z[1]: [0,1){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: store i8 5, i8* %x
+; GLOBAL-NEXT: %n = load i8, i8* %y
+; GLOBAL-NEXT: call void @llvm.memset.p0i8.i32(i8* nonnull %z, i8 0, i32 1, i1 false)
 ; CHECK-EMPTY:
 entry:
   %x = alloca i8, align 4
@@ -587,6 +638,7 @@ define void @LifeEnded() {
 ; CHECK: x[1]: full-set{{$}}
 ; CHECK: y[1]: full-set{{$}}
 ; CHECK: z[1]: full-set{{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i8, align 4
@@ -608,5 +660,236 @@ entry:
   ret void
 }
 
+define void @TwoAllocasOK() {
+; CHECK-LABEL: @TwoAllocasOK
+; CHECK-NEXT: args uses:
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: [0,1){{$}}
+; CHECK: y[1]: [0,1){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %y, i8* %x, i32 1, i1 false)
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  %x = bitcast i32* %a to i8*
+  %y = alloca i8, align 4
+  call void @llvm.memcpy.p0i8.p0i8.i32(i8* %y, i8* %x, i32 1, i1 false)
+  ret void
+}
+
+define void @TwoAllocasOOBDest() {
+; CHECK-LABEL: @TwoAllocasOOBDest
+; CHECK-NEXT: args uses:
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: [0,4){{$}}
+; CHECK: y[1]: [0,4){{$}}
+; GLOBAL-NEXT: safe accesses:
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  %x = bitcast i32* %a to i8*
+  %y = alloca i8, align 4
+  call void @llvm.memcpy.p0i8.p0i8.i32(i8* %y, i8* %x, i32 4, i1 false)
+  ret void
+}
+
+define void @TwoAllocasOOBSource() {
+; CHECK-LABEL: @TwoAllocasOOBSource
+; CHECK-NEXT: args uses:
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: [0,4){{$}}
+; CHECK: y[1]: [0,4){{$}}
+; GLOBAL-NEXT: safe accesses:
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  %x = bitcast i32* %a to i8*
+  %y = alloca i8, align 4
+  call void @llvm.memcpy.p0i8.p0i8.i32(i8* %x, i8* %y, i32 4, i1 false)
+  ret void
+}
+
+define void @TwoAllocasOOBBoth() {
+; CHECK-LABEL: @TwoAllocasOOBBoth
+; CHECK-NEXT: args uses:
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: [0,5){{$}}
+; CHECK: y[1]: [0,5){{$}}
+; GLOBAL-NEXT: safe accesses:
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  %x = bitcast i32* %a to i8*
+  %y = alloca i8, align 4
+  call void @llvm.memcpy.p0i8.p0i8.i32(i8* %y, i8* %x, i32 5, i1 false)
+  ret void
+}
+
+define void @MixedAccesses() {
+; CHECK-LABEL: @MixedAccesses
+; CHECK-NEXT: args uses:
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: [0,5){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false)
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  %x = bitcast i32* %a to i8*
+  call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 5, i1 false)
+  call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false)
+  ret void
+}
+
+define void @MixedAccesses2() {
+; CHECK-LABEL: @MixedAccesses2
+; CHECK-NEXT: args uses:
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: [0,8){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: load i32, i32* %a, align 4
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  %a1 = bitcast i32* %a to i64*
+  %n1 = load i64, i64* %a1, align 4
+  %n2 = load i32, i32* %a, align 4
+  ret void
+}
+
+define void @MixedAccesses3(void (i8*)* %func) {
+; CHECK-LABEL: @MixedAccesses3
+; CHECK-NEXT: args uses:
+; CHECK-NEXT: func[]: full-set
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: full-set{{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: load i32, i32* %a, align 4
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  %x = bitcast i32* %a to i8*
+  %n2 = load i32, i32* %a, align 4
+  call void %func(i8* %x)
+  ret void
+}
+
+define void @MixedAccesses4() {
+; CHECK-LABEL: @MixedAccesses4
+; CHECK-NEXT: args uses:
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: full-set{{$}}
+; CHECK: a1[8]: [0,8){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: load i32, i32* %a, align 4
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  %a1 = alloca i32*, align 4
+  %n2 = load i32, i32* %a, align 4
+  store i32* %a, i32** %a1
+  ret void
+}
+
+define i32* @MixedAccesses5(i1 %x, i32* %y) {
+; CHECK-LABEL: @MixedAccesses5
+; CHECK-NEXT: args uses:
+; CHECK: y[]: full-set
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: full-set{{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: load i32, i32* %a, align 4
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  br i1 %x, label %tlabel, label %flabel
+flabel:
+  %n = load i32, i32* %a, align 4
+  ret i32* %y
+tlabel:
+  ret i32* %a
+}
+
+define void @DoubleLifetime() {
+; CHECK-LABEL: @DoubleLifetime
+; CHECK-NEXT: args uses:
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: full-set{{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false)
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  %x = bitcast i32* %a to i8*
+  call void @llvm.lifetime.start.p0i8(i64 4, i8* %x)
+  call void @llvm.lifetime.end.p0i8(i64 4, i8* %x)
+  call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 true)
+
+  call void @llvm.lifetime.start.p0i8(i64 4, i8* %x)
+  call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false)
+  call void @llvm.lifetime.end.p0i8(i64 4, i8* %x)
+  ret void
+}
+
+define void @DoubleLifetime2() {
+; CHECK-LABEL: @DoubleLifetime2
+; CHECK-NEXT: args uses:
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: full-set{{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false)
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  %x = bitcast i32* %a to i8*
+  call void @llvm.lifetime.start.p0i8(i64 4, i8* %x)
+  call void @llvm.lifetime.end.p0i8(i64 4, i8* %x)
+  %n = load i32, i32* %a
+
+  call void @llvm.lifetime.start.p0i8(i64 4, i8* %x)
+  call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false)
+  call void @llvm.lifetime.end.p0i8(i64 4, i8* %x)
+  ret void
+}
+
+define void @DoubleLifetime3() {
+; CHECK-LABEL: @DoubleLifetime3
+; CHECK-NEXT: args uses:
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: full-set{{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false)
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  %x = bitcast i32* %a to i8*
+  call void @llvm.lifetime.start.p0i8(i64 4, i8* %x)
+  call void @llvm.lifetime.end.p0i8(i64 4, i8* %x)
+  store i32 5, i32* %a
+
+  call void @llvm.lifetime.start.p0i8(i64 4, i8* %x)
+  call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false)
+  call void @llvm.lifetime.end.p0i8(i64 4, i8* %x)
+  ret void
+}
+
+define void @DoubleLifetime4() {
+; CHECK-LABEL: @DoubleLifetime4
+; CHECK-NEXT: args uses:
+; CHECK-NEXT: allocas uses:
+; CHECK: a[4]: full-set{{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false)
+; CHECK-EMPTY:
+entry:
+  %a = alloca i32, align 4
+  %x = bitcast i32* %a to i8*
+  call void @llvm.lifetime.start.p0i8(i64 4, i8* %x)
+  call void @llvm.memset.p0i8.i32(i8* %x, i8 1, i32 4, i1 false)
+  call void @llvm.lifetime.end.p0i8(i64 4, i8* %x)
+  call void @unknown_call(i8* %x)
+  ret void
+}
+
 declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
 declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)

diff  --git a/llvm/test/Analysis/StackSafetyAnalysis/memintrin.ll b/llvm/test/Analysis/StackSafetyAnalysis/memintrin.ll
index 3d458cbfeff8c..a19e3ed46efb0 100644
--- a/llvm/test/Analysis/StackSafetyAnalysis/memintrin.ll
+++ b/llvm/test/Analysis/StackSafetyAnalysis/memintrin.ll
@@ -1,5 +1,5 @@
 ; RUN: opt -S -passes="print<stack-safety-local>" -disable-output < %s 2>&1 | FileCheck %s
-; RUN: opt -S -passes="print-stack-safety" -disable-output < %s 2>&1 | FileCheck %s
+; RUN: opt -S -passes="print-stack-safety" -disable-output < %s 2>&1 | FileCheck %s --check-prefixes=CHECK,GLOBAL
 
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 target triple = "x86_64-unknown-linux-gnu"
@@ -14,6 +14,8 @@ define void @MemsetInBounds() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,4){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: call void @llvm.memset.p0i8.i32(i8* %x1, i8 42, i32 4, i1 false)
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -28,6 +30,8 @@ define void @VolatileMemsetInBounds() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,4){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: call void @llvm.memset.p0i8.i32(i8* %x1, i8 42, i32 4, i1 true)
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -41,6 +45,7 @@ define void @MemsetOutOfBounds() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,5){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -54,6 +59,7 @@ define void @MemsetNonConst(i32 %size) {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,4294967295){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -69,6 +75,7 @@ define void @MemsetNonConstInBounds(i1 zeroext %z) {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,7){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -84,6 +91,7 @@ define void @MemsetNonConstSize() {
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,4294967295){{$}}
 ; CHECK-NEXT: y[4]: empty-set{{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -102,6 +110,8 @@ define void @MemcpyInBounds() {
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,4){{$}}
 ; CHECK-NEXT: y[4]: [0,4){{$}}
+; GLOBAL-NEXT: safe accesses:
+; GLOBAL-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %x1, i8* %y1, i32 4, i1 false)
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -118,6 +128,7 @@ define void @MemcpySrcOutOfBounds() {
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[8]: [0,5){{$}}
 ; CHECK-NEXT: y[4]: [0,5){{$}}
+; GLOBAL-NEXT: safe accesses
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -134,6 +145,7 @@ define void @MemcpyDstOutOfBounds() {
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,5){{$}}
 ; CHECK-NEXT: y[8]: [0,5){{$}}
+; GLOBAL-NEXT: safe accesses
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -150,6 +162,7 @@ define void @MemcpyBothOutOfBounds() {
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[4]: [0,9){{$}}
 ; CHECK-NEXT: y[8]: [0,9){{$}}
+; GLOBAL-NEXT: safe accesses
 ; CHECK-EMPTY:
 entry:
   %x = alloca i32, align 4
@@ -165,6 +178,8 @@ define void @MemcpySelfInBounds() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[8]: [0,8){{$}}
+; GLOBAL-NEXT: safe accesses
+; GLOBAL-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %x1, i8* %x2, i32 3, i1 false)
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -179,6 +194,7 @@ define void @MemcpySelfSrcOutOfBounds() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[8]: [0,9){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -193,6 +209,7 @@ define void @MemcpySelfDstOutOfBounds() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[8]: [0,9){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4
@@ -207,6 +224,7 @@ define void @MemmoveSelfBothOutOfBounds() {
 ; CHECK-NEXT: args uses:
 ; CHECK-NEXT: allocas uses:
 ; CHECK-NEXT: x[8]: [0,14){{$}}
+; GLOBAL-NEXT: safe accesses:
 ; CHECK-EMPTY:
 entry:
   %x = alloca i64, align 4


        


More information about the llvm-commits mailing list