Blame 0001-MemCpyOpt-Correctly-merge-alias-scopes-during-call-s.patch

76f7a99
From e804574cad8efa1b7a660848ef7adc871a7f850e Mon Sep 17 00:00:00 2001
76f7a99
From: modimo <modimo@fb.com>
76f7a99
Date: Thu, 3 Dec 2020 09:23:37 -0800
76f7a99
Subject: [PATCH] [MemCpyOpt] Correctly merge alias scopes during call slot
76f7a99
 optimization
76f7a99
76f7a99
When MemCpyOpt performs call slot optimization it will concatenate the `alias.scope` metadata between the function call and the memcpy. However, scoped AA relies on the domains in metadata to be maintained in a caller-callee relationship. Naive concatenation breaks this assumption leading to bad AA results.
76f7a99
76f7a99
The fix is to take the intersection of domains then union the scopes within those domains.
76f7a99
76f7a99
The original bug came from a case of rust bad codegen which uses this bad aliasing to perform additional memcpy optimizations. As show in the added test case `%src` got forwarded past its lifetime leading to a dereference of garbage data.
76f7a99
76f7a99
Testing
76f7a99
ninja check-llvm
76f7a99
76f7a99
Reviewed By: jeroen.dobbelaere
76f7a99
76f7a99
Differential Revision: https://reviews.llvm.org/D91576
76f7a99
76f7a99
(cherry picked from commit 18603319321a6c1b158800bcc60035ee01549516)
76f7a99
---
76f7a99
 llvm/include/llvm/Analysis/ScopedNoAliasAA.h  | 21 ++++++++++
76f7a99
 llvm/lib/Analysis/ScopedNoAliasAA.cpp         | 25 ------------
76f7a99
 llvm/lib/IR/Metadata.cpp                      | 28 ++++++++++++-
76f7a99
 .../ScopedNoAliasAA/alias-scope-merging.ll    | 37 ++++++++++++++++++
76f7a99
 llvm/test/Transforms/GVN/noalias.ll           | 29 +++++++-------
76f7a99
 .../InstCombine/fold-phi-load-metadata.ll     |  4 +-
76f7a99
 .../Transforms/MemCpyOpt/callslot_badaa.ll    | 39 +++++++++++++++++++
76f7a99
 llvm/test/Transforms/NewGVN/noalias.ll        | 29 +++++++-------
76f7a99
 8 files changed, 156 insertions(+), 56 deletions(-)
76f7a99
 create mode 100644 llvm/test/Analysis/ScopedNoAliasAA/alias-scope-merging.ll
76f7a99
 create mode 100644 llvm/test/Transforms/MemCpyOpt/callslot_badaa.ll
76f7a99
76f7a99
diff --git a/llvm/include/llvm/Analysis/ScopedNoAliasAA.h b/llvm/include/llvm/Analysis/ScopedNoAliasAA.h
76f7a99
index c55228eace4b..562640647918 100644
76f7a99
--- a/llvm/include/llvm/Analysis/ScopedNoAliasAA.h
76f7a99
+++ b/llvm/include/llvm/Analysis/ScopedNoAliasAA.h
76f7a99
@@ -25,6 +25,27 @@ class Function;
76f7a99
 class MDNode;
76f7a99
 class MemoryLocation;
76f7a99
 
76f7a99
+/// This is a simple wrapper around an MDNode which provides a higher-level
76f7a99
+/// interface by hiding the details of how alias analysis information is encoded
76f7a99
+/// in its operands.
76f7a99
+class AliasScopeNode {
76f7a99
+  const MDNode *Node = nullptr;
76f7a99
+
76f7a99
+public:
76f7a99
+  AliasScopeNode() = default;
76f7a99
+  explicit AliasScopeNode(const MDNode *N) : Node(N) {}
76f7a99
+
76f7a99
+  /// Get the MDNode for this AliasScopeNode.
76f7a99
+  const MDNode *getNode() const { return Node; }
76f7a99
+
76f7a99
+  /// Get the MDNode for this AliasScopeNode's domain.
76f7a99
+  const MDNode *getDomain() const {
76f7a99
+    if (Node->getNumOperands() < 2)
76f7a99
+      return nullptr;
76f7a99
+    return dyn_cast_or_null<MDNode>(Node->getOperand(1));
76f7a99
+  }
76f7a99
+};
76f7a99
+
76f7a99
 /// A simple AA result which uses scoped-noalias metadata to answer queries.
76f7a99
 class ScopedNoAliasAAResult : public AAResultBase<ScopedNoAliasAAResult> {
76f7a99
   friend AAResultBase<ScopedNoAliasAAResult>;
76f7a99
diff --git a/llvm/lib/Analysis/ScopedNoAliasAA.cpp b/llvm/lib/Analysis/ScopedNoAliasAA.cpp
76f7a99
index 8928678d6ab2..22e0501b28f4 100644
76f7a99
--- a/llvm/lib/Analysis/ScopedNoAliasAA.cpp
76f7a99
+++ b/llvm/lib/Analysis/ScopedNoAliasAA.cpp
76f7a99
@@ -50,31 +50,6 @@ using namespace llvm;
76f7a99
 static cl::opt<bool> EnableScopedNoAlias("enable-scoped-noalias",
76f7a99
                                          cl::init(true), cl::Hidden);
76f7a99
 
76f7a99
-namespace {
76f7a99
-
76f7a99
-/// This is a simple wrapper around an MDNode which provides a higher-level
76f7a99
-/// interface by hiding the details of how alias analysis information is encoded
76f7a99
-/// in its operands.
76f7a99
-class AliasScopeNode {
76f7a99
-  const MDNode *Node = nullptr;
76f7a99
-
76f7a99
-public:
76f7a99
-  AliasScopeNode() = default;
76f7a99
-  explicit AliasScopeNode(const MDNode *N) : Node(N) {}
76f7a99
-
76f7a99
-  /// Get the MDNode for this AliasScopeNode.
76f7a99
-  const MDNode *getNode() const { return Node; }
76f7a99
-
76f7a99
-  /// Get the MDNode for this AliasScopeNode's domain.
76f7a99
-  const MDNode *getDomain() const {
76f7a99
-    if (Node->getNumOperands() < 2)
76f7a99
-      return nullptr;
76f7a99
-    return dyn_cast_or_null<MDNode>(Node->getOperand(1));
76f7a99
-  }
76f7a99
-};
76f7a99
-
76f7a99
-} // end anonymous namespace
76f7a99
-
76f7a99
 AliasResult ScopedNoAliasAAResult::alias(const MemoryLocation &LocA,
76f7a99
                                          const MemoryLocation &LocB,
76f7a99
                                          AAQueryInfo &AAQI) {
76f7a99
diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp
76f7a99
index ce89009e86eb..5826464206d6 100644
76f7a99
--- a/llvm/lib/IR/Metadata.cpp
76f7a99
+++ b/llvm/lib/IR/Metadata.cpp
76f7a99
@@ -26,6 +26,7 @@
76f7a99
 #include "llvm/ADT/StringMap.h"
76f7a99
 #include "llvm/ADT/StringRef.h"
76f7a99
 #include "llvm/ADT/Twine.h"
76f7a99
+#include "llvm/Analysis/ScopedNoAliasAA.h"
76f7a99
 #include "llvm/IR/Argument.h"
76f7a99
 #include "llvm/IR/BasicBlock.h"
76f7a99
 #include "llvm/IR/Constant.h"
76f7a99
@@ -925,7 +926,32 @@ MDNode *MDNode::getMostGenericAliasScope(MDNode *A, MDNode *B) {
76f7a99
   if (!A || !B)
76f7a99
     return nullptr;
76f7a99
 
76f7a99
-  return concatenate(A, B);
76f7a99
+  // Take the intersection of domains then union the scopes
76f7a99
+  // within those domains
76f7a99
+  SmallPtrSet<const MDNode *, 16> ADomains;
76f7a99
+  SmallPtrSet<const MDNode *, 16> IntersectDomains;
76f7a99
+  SmallSetVector<Metadata *, 4> MDs;
76f7a99
+  for (const MDOperand &MDOp : A->operands())
76f7a99
+    if (const MDNode *NAMD = dyn_cast<MDNode>(MDOp))
76f7a99
+      if (const MDNode *Domain = AliasScopeNode(NAMD).getDomain())
76f7a99
+        ADomains.insert(Domain);
76f7a99
+
76f7a99
+  for (const MDOperand &MDOp : B->operands())
76f7a99
+    if (const MDNode *NAMD = dyn_cast<MDNode>(MDOp))
76f7a99
+      if (const MDNode *Domain = AliasScopeNode(NAMD).getDomain())
76f7a99
+        if (ADomains.contains(Domain)) {
76f7a99
+          IntersectDomains.insert(Domain);
76f7a99
+          MDs.insert(MDOp);
76f7a99
+        }
76f7a99
+
76f7a99
+  for (const MDOperand &MDOp : A->operands())
76f7a99
+    if (const MDNode *NAMD = dyn_cast<MDNode>(MDOp))
76f7a99
+      if (const MDNode *Domain = AliasScopeNode(NAMD).getDomain())
76f7a99
+        if (IntersectDomains.contains(Domain))
76f7a99
+          MDs.insert(MDOp);
76f7a99
+
76f7a99
+  return MDs.empty() ? nullptr
76f7a99
+                     : getOrSelfReference(A->getContext(), MDs.getArrayRef());
76f7a99
 }
76f7a99
 
76f7a99
 MDNode *MDNode::getMostGenericFPMath(MDNode *A, MDNode *B) {
76f7a99
diff --git a/llvm/test/Analysis/ScopedNoAliasAA/alias-scope-merging.ll b/llvm/test/Analysis/ScopedNoAliasAA/alias-scope-merging.ll
76f7a99
new file mode 100644
76f7a99
index 000000000000..4c8369d30adb
76f7a99
--- /dev/null
76f7a99
+++ b/llvm/test/Analysis/ScopedNoAliasAA/alias-scope-merging.ll
76f7a99
@@ -0,0 +1,37 @@
76f7a99
+; RUN: opt < %s -S -memcpyopt | FileCheck --match-full-lines %s
76f7a99
+
76f7a99
+; Alias scopes are merged by taking the intersection of domains, then the union of the scopes within those domains
76f7a99
+define i8 @test(i8 %input) {
76f7a99
+  %tmp = alloca i8
76f7a99
+  %dst = alloca i8
76f7a99
+  %src = alloca i8
76f7a99
+; CHECK:   call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %dst, i8* align 8 %src, i64 1, i1 false), !alias.scope ![[SCOPE:[0-9]+]]
76f7a99
+  call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %src), !noalias !4
76f7a99
+  store i8 %input, i8* %src
76f7a99
+  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %tmp, i8* align 8 %src, i64 1, i1 false), !alias.scope !0
76f7a99
+  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %src), !noalias !4
76f7a99
+  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %dst, i8* align 8 %tmp, i64 1, i1 false), !alias.scope !4
76f7a99
+  %ret_value = load i8, i8* %dst
76f7a99
+  ret i8 %ret_value
76f7a99
+}
76f7a99
+
76f7a99
+; Merged scope contains "callee0: %a" and "callee0 : %b"
76f7a99
+; CHECK-DAG: ![[CALLEE0_A:[0-9]+]] = distinct !{!{{[0-9]+}}, !{{[0-9]+}}, !"callee0: %a"}
76f7a99
+; CHECK-DAG: ![[CALLEE0_B:[0-9]+]] = distinct !{!{{[0-9]+}}, !{{[0-9]+}}, !"callee0: %b"}
76f7a99
+; CHECK-DAG: ![[SCOPE]] = !{![[CALLEE0_A]], ![[CALLEE0_B]]}
76f7a99
+
76f7a99
+declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
76f7a99
+declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
76f7a99
+declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1)
76f7a99
+
76f7a99
+!0 = !{!1, !7}
76f7a99
+!1 = distinct !{!1, !3, !"callee0: %a"}
76f7a99
+!2 = distinct !{!2, !3, !"callee0: %b"}
76f7a99
+!3 = distinct !{!3, !"callee0"}
76f7a99
+
76f7a99
+!4 = !{!2, !5}
76f7a99
+!5 = distinct !{!5, !6, !"callee1: %a"}
76f7a99
+!6 = distinct !{!6, !"callee1"}
76f7a99
+
76f7a99
+!7 = distinct !{!7, !8, !"callee2: %a"}
76f7a99
+!8 = distinct !{!8, !"callee2"}
76f7a99
diff --git a/llvm/test/Transforms/GVN/noalias.ll b/llvm/test/Transforms/GVN/noalias.ll
76f7a99
index 69c21f110b5e..67d48d768a91 100644
76f7a99
--- a/llvm/test/Transforms/GVN/noalias.ll
76f7a99
+++ b/llvm/test/Transforms/GVN/noalias.ll
76f7a99
@@ -5,7 +5,7 @@ define i32 @test1(i32* %p, i32* %q) {
76f7a99
 ; CHECK: load i32, i32* %p
76f7a99
 ; CHECK-NOT: noalias
76f7a99
 ; CHECK: %c = add i32 %a, %a
76f7a99
-  %a = load i32, i32* %p, !noalias !0
76f7a99
+  %a = load i32, i32* %p, !noalias !3
76f7a99
   %b = load i32, i32* %p
76f7a99
   %c = add i32 %a, %b
76f7a99
   ret i32 %c
76f7a99
@@ -13,31 +13,32 @@ define i32 @test1(i32* %p, i32* %q) {
76f7a99
 
76f7a99
 define i32 @test2(i32* %p, i32* %q) {
76f7a99
 ; CHECK-LABEL: @test2(i32* %p, i32* %q)
76f7a99
-; CHECK: load i32, i32* %p, align 4, !alias.scope !0
76f7a99
+; CHECK: load i32, i32* %p, align 4, !alias.scope ![[SCOPE1:[0-9]+]]
76f7a99
 ; CHECK: %c = add i32 %a, %a
76f7a99
-  %a = load i32, i32* %p, !alias.scope !0
76f7a99
-  %b = load i32, i32* %p, !alias.scope !0
76f7a99
+  %a = load i32, i32* %p, !alias.scope !3
76f7a99
+  %b = load i32, i32* %p, !alias.scope !3
76f7a99
   %c = add i32 %a, %b
76f7a99
   ret i32 %c
76f7a99
 }
76f7a99
 
76f7a99
-; FIXME: In this case we can do better than intersecting the scopes, and can
76f7a99
-; concatenate them instead. Both loads are in the same basic block, the first
76f7a99
-; makes the second safe to speculatively execute, and there are no calls that may
76f7a99
-; throw in between.
76f7a99
 define i32 @test3(i32* %p, i32* %q) {
76f7a99
 ; CHECK-LABEL: @test3(i32* %p, i32* %q)
76f7a99
-; CHECK: load i32, i32* %p, align 4, !alias.scope !1
76f7a99
+; CHECK: load i32, i32* %p, align 4, !alias.scope ![[SCOPE2:[0-9]+]]
76f7a99
 ; CHECK: %c = add i32 %a, %a
76f7a99
-  %a = load i32, i32* %p, !alias.scope !1
76f7a99
-  %b = load i32, i32* %p, !alias.scope !2
76f7a99
+  %a = load i32, i32* %p, !alias.scope !4
76f7a99
+  %b = load i32, i32* %p, !alias.scope !5
76f7a99
   %c = add i32 %a, %b
76f7a99
   ret i32 %c
76f7a99
 }
76f7a99
 
76f7a99
+; CHECK:   ![[SCOPE1]] = !{!{{[0-9]+}}}
76f7a99
+; CHECK:   ![[SCOPE2]] = !{!{{[0-9]+}}, !{{[0-9]+}}}
76f7a99
 declare i32 @foo(i32*) readonly
76f7a99
 
76f7a99
-!0 = !{!0}
76f7a99
-!1 = !{!1}
76f7a99
-!2 = !{!0, !1}
76f7a99
+!0 = distinct !{!0, !2, !"callee0: %a"}
76f7a99
+!1 = distinct !{!1, !2, !"callee0: %b"}
76f7a99
+!2 = distinct !{!2, !"callee0"}
76f7a99
 
76f7a99
+!3 = !{!0}
76f7a99
+!4 = !{!1}
76f7a99
+!5 = !{!0, !1}
76f7a99
diff --git a/llvm/test/Transforms/InstCombine/fold-phi-load-metadata.ll b/llvm/test/Transforms/InstCombine/fold-phi-load-metadata.ll
76f7a99
index e5a1aa7362a5..7fa26b46e25d 100644
76f7a99
--- a/llvm/test/Transforms/InstCombine/fold-phi-load-metadata.ll
76f7a99
+++ b/llvm/test/Transforms/InstCombine/fold-phi-load-metadata.ll
76f7a99
@@ -40,10 +40,10 @@ return:                                           ; preds = %if.end, %if.then
76f7a99
 ; CHECK: ![[TBAA]] = !{![[TAG1:[0-9]+]], ![[TAG1]], i64 0}
76f7a99
 ; CHECK: ![[TAG1]] = !{!"int", !{{[0-9]+}}, i64 0}
76f7a99
 ; CHECK: ![[RANGE]] = !{i32 10, i32 25}
76f7a99
-; CHECK: ![[ALIAS_SCOPE]] = !{![[SCOPE0:[0-9]+]], ![[SCOPE2:[0-9]+]], ![[SCOPE1:[0-9]+]]}
76f7a99
+; CHECK: ![[ALIAS_SCOPE]] = !{![[SCOPE0:[0-9]+]], ![[SCOPE1:[0-9]+]], ![[SCOPE2:[0-9]+]]}
76f7a99
 ; CHECK: ![[SCOPE0]] = distinct !{![[SCOPE0]], !{{[0-9]+}}, !"scope0"}
76f7a99
-; CHECK: ![[SCOPE2]] = distinct !{![[SCOPE2]], !{{[0-9]+}}, !"scope2"}
76f7a99
 ; CHECK: ![[SCOPE1]] = distinct !{![[SCOPE1]], !{{[0-9]+}}, !"scope1"}
76f7a99
+; CHECK: ![[SCOPE2]] = distinct !{![[SCOPE2]], !{{[0-9]+}}, !"scope2"}
76f7a99
 ; CHECK: ![[NOALIAS]] = !{![[SCOPE3:[0-9]+]]}
76f7a99
 ; CHECK: ![[SCOPE3]] = distinct !{![[SCOPE3]], !{{[0-9]+}}, !"scope3"}
76f7a99
 
76f7a99
diff --git a/llvm/test/Transforms/MemCpyOpt/callslot_badaa.ll b/llvm/test/Transforms/MemCpyOpt/callslot_badaa.ll
76f7a99
new file mode 100644
76f7a99
index 000000000000..346546f72c4c
76f7a99
--- /dev/null
76f7a99
+++ b/llvm/test/Transforms/MemCpyOpt/callslot_badaa.ll
76f7a99
@@ -0,0 +1,39 @@
76f7a99
+; RUN: opt < %s -S -memcpyopt | FileCheck --match-full-lines %s
76f7a99
+
76f7a99
+; Make sure callslot optimization merges alias.scope metadata correctly when it merges instructions.
76f7a99
+; Merging here naively generates:
76f7a99
+;  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %dst, i8* align 8 %src, i64 1, i1 false), !alias.scope !3
76f7a99
+;  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %src), !noalias !0
76f7a99
+;   ...
76f7a99
+;  !0 = !{!1}
76f7a99
+;  !1 = distinct !{!1, !2, !"callee1: %a"}
76f7a99
+;  !2 = distinct !{!2, !"callee1"}
76f7a99
+;  !3 = !{!1, !4}
76f7a99
+;  !4 = distinct !{!4, !5, !"callee0: %a"}
76f7a99
+;  !5 = distinct !{!5, !"callee0"}
76f7a99
+; Which is incorrect because the lifetime.end of %src will now "noalias" the above memcpy.
76f7a99
+define i8 @test(i8 %input) {
76f7a99
+  %tmp = alloca i8
76f7a99
+  %dst = alloca i8
76f7a99
+  %src = alloca i8
76f7a99
+; NOTE: we're matching the full line and looking for the lack of !alias.scope here
76f7a99
+; CHECK:   call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %dst, i8* align 8 %src, i64 1, i1 false)
76f7a99
+  call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %src), !noalias !3
76f7a99
+  store i8 %input, i8* %src
76f7a99
+  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %tmp, i8* align 8 %src, i64 1, i1 false), !alias.scope !0
76f7a99
+  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %src), !noalias !3
76f7a99
+  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %dst, i8* align 8 %tmp, i64 1, i1 false), !alias.scope !3
76f7a99
+  %ret_value = load i8, i8* %dst
76f7a99
+  ret i8 %ret_value
76f7a99
+}
76f7a99
+
76f7a99
+declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
76f7a99
+declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
76f7a99
+declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1)
76f7a99
+
76f7a99
+!0 = !{!1}
76f7a99
+!1 = distinct !{!1, !2, !"callee0: %a"}
76f7a99
+!2 = distinct !{!2, !"callee0"}
76f7a99
+!3 = !{!4}
76f7a99
+!4 = distinct !{!4, !5, !"callee1: %a"}
76f7a99
+!5 = distinct !{!5, !"callee1"}
76f7a99
diff --git a/llvm/test/Transforms/NewGVN/noalias.ll b/llvm/test/Transforms/NewGVN/noalias.ll
76f7a99
index c5f23bfad89a..2d90dc84d90b 100644
76f7a99
--- a/llvm/test/Transforms/NewGVN/noalias.ll
76f7a99
+++ b/llvm/test/Transforms/NewGVN/noalias.ll
76f7a99
@@ -5,7 +5,7 @@ define i32 @test1(i32* %p, i32* %q) {
76f7a99
 ; CHECK: load i32, i32* %p
76f7a99
 ; CHECK-NOT: noalias
76f7a99
 ; CHECK: %c = add i32 %a, %a
76f7a99
-  %a = load i32, i32* %p, !noalias !0
76f7a99
+  %a = load i32, i32* %p, !noalias !3
76f7a99
   %b = load i32, i32* %p
76f7a99
   %c = add i32 %a, %b
76f7a99
   ret i32 %c
76f7a99
@@ -13,31 +13,32 @@ define i32 @test1(i32* %p, i32* %q) {
76f7a99
 
76f7a99
 define i32 @test2(i32* %p, i32* %q) {
76f7a99
 ; CHECK-LABEL: @test2(i32* %p, i32* %q)
76f7a99
-; CHECK: load i32, i32* %p, align 4, !alias.scope !0
76f7a99
+; CHECK: load i32, i32* %p, align 4, !alias.scope ![[SCOPE1:[0-9]+]]
76f7a99
 ; CHECK: %c = add i32 %a, %a
76f7a99
-  %a = load i32, i32* %p, !alias.scope !0
76f7a99
-  %b = load i32, i32* %p, !alias.scope !0
76f7a99
+  %a = load i32, i32* %p, !alias.scope !3
76f7a99
+  %b = load i32, i32* %p, !alias.scope !3
76f7a99
   %c = add i32 %a, %b
76f7a99
   ret i32 %c
76f7a99
 }
76f7a99
 
76f7a99
-; FIXME: In this case we can do better than intersecting the scopes, and can
76f7a99
-; concatenate them instead. Both loads are in the same basic block, the first
76f7a99
-; makes the second safe to speculatively execute, and there are no calls that may
76f7a99
-; throw in between.
76f7a99
 define i32 @test3(i32* %p, i32* %q) {
76f7a99
 ; CHECK-LABEL: @test3(i32* %p, i32* %q)
76f7a99
-; CHECK: load i32, i32* %p, align 4, !alias.scope !1
76f7a99
+; CHECK: load i32, i32* %p, align 4, !alias.scope ![[SCOPE2:[0-9]+]]
76f7a99
 ; CHECK: %c = add i32 %a, %a
76f7a99
-  %a = load i32, i32* %p, !alias.scope !1
76f7a99
-  %b = load i32, i32* %p, !alias.scope !2
76f7a99
+  %a = load i32, i32* %p, !alias.scope !4
76f7a99
+  %b = load i32, i32* %p, !alias.scope !5
76f7a99
   %c = add i32 %a, %b
76f7a99
   ret i32 %c
76f7a99
 }
76f7a99
 
76f7a99
+; CHECK:   ![[SCOPE1]] = !{!{{[0-9]+}}}
76f7a99
+; CHECK:   ![[SCOPE2]] = !{!{{[0-9]+}}, !{{[0-9]+}}}
76f7a99
 declare i32 @foo(i32*) readonly
76f7a99
 
76f7a99
-!0 = !{!0}
76f7a99
-!1 = !{!1}
76f7a99
-!2 = !{!0, !1}
76f7a99
+!0 = distinct !{!0, !2, !"callee0: %a"}
76f7a99
+!1 = distinct !{!1, !2, !"callee0: %b"}
76f7a99
+!2 = distinct !{!2, !"callee0"}
76f7a99
 
76f7a99
+!3 = !{!0}
76f7a99
+!4 = !{!1}
76f7a99
+!5 = !{!0, !1}
76f7a99
-- 
76f7a99
2.30.2
76f7a99