Simplify and improve scoped-noalias metadata semantics
authorHal Finkel <hfinkel@anl.gov>
Fri, 25 Jul 2014 15:50:02 +0000 (15:50 +0000)
committerHal Finkel <hfinkel@anl.gov>
Fri, 25 Jul 2014 15:50:02 +0000 (15:50 +0000)
In the process of fixing the noalias parameter -> metadata conversion process
that will take place during inlining (which will be committed soon, but not
turned on by default), I have come to realize that the semantics provided by
yesterday's commit are not really what we want. Here's why:

void foo(noalias a, noalias b, noalias c, bool x) {
  *q = x ? a : b;
  *c = *q;
}

Generically, we know that *c does not alias with *a and with *b (so there is an
'and' in what we know we're not), and we know that *q might be derived from *a
or from *b (so there is an 'or' in what we know that we are). So we do not want
the semantics currently, where any noalias scope matching any alias.scope
causes a NoAlias return. What we want to know is that the noalias scopes form a
superset of the alias.scope list (meaning that all the things we know we're not
is a superset of all of things the other instruction might be).

Making that change, however, introduces a composibility problem. If we inline
once, adding the noalias metadata, and then inline again adding more, and we
append new scopes onto the noalias and alias.scope lists each time. But, this
means that we could change what was a NoAlias result previously into a MayAlias
result because we appended an additional scope onto one of the alias.scope
lists. So, instead of giving scopes the ability to have parents (which I had
borrowed from the TBAA implementation, but seems increasingly unlikely to be
useful in practice), I've given them domains. The subset/superset condition now
applies within each domain independently, and we only need it to hold in one
domain. Each time we inline, we add the new scopes in a new scope domain, and
everything now composes nicely. In addition, this simplifies the
implementation.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@213948 91177308-0d34-0410-b5e6-96231b3b80d8

docs/LangRef.rst
include/llvm/IR/MDBuilder.h
lib/Analysis/ScopedNoAliasAA.cpp
lib/IR/MDBuilder.cpp
test/Analysis/ScopedNoAliasAA/basic-domains.ll [new file with mode: 0644]
test/Analysis/ScopedNoAliasAA/basic.ll
test/Analysis/ScopedNoAliasAA/basic2.ll

index ddbf40034942be7de670f2f60522ed7a951fc2aa..891b9c0af70a10f6b3f1f5b9aa78394411d03c08 100644 (file)
@@ -2837,50 +2837,58 @@ noalias memory-access sets. This means that some collection of memory access
 instructions (loads, stores, memory-accessing calls, etc.) that carry
 ``noalias`` metadata can specifically be specified not to alias with some other
 collection of memory access instructions that carry ``alias.scope`` metadata.
-Each type of metadata specifies a list of scopes, and when evaluating an
-aliasing query, if one of the instructions has a scope in its ``alias.scope``
-list that is identical to a scope in the other instruction's ``noalias`` list,
-or is a descendant (in the scope hierarchy) of a scope in the other
-instruction's ``noalias`` list , then the two memory accesses are assumed not
-to alias.
-
-The metadata identifying each scope is itself a list containing one or two
-entries. The first entry is the name of the scope. Note that if the name is a
+Each type of metadata specifies a list of scopes where each scope has an id and
+a domain. When evaluating an aliasing query, if for some some domain, the set
+of scopes with that domain in one instruction's ``alias.scope`` list is a
+subset of (or qual to) the set of scopes for that domain in another
+instruction's ``noalias`` list, then the two memory accesses are assumed not to
+alias.
+
+The metadata identifying each domain is itself a list containing one or two
+entries. The first entry is the name of the domain. Note that if the name is a
 string then it can be combined accross functions and translation units. A
-self-reference can be used to create globally unique scope names.
-Optionally, a metadata reference to a parent scope can be provided as a second
-entry in the list.
+self-reference can be used to create globally unique domain names. A
+descriptive string may optionally be provided as a second list entry.
+
+The metadata identifying each scope is also itself a list containing two or
+three entries. The first entry is the name of the scope. Note that if the name
+is a string then it can be combined accross functions and translation units. A
+self-reference can be used to create globally unique scope names. A metadata
+reference to the scope's domain is the second entry. A descriptive string may
+optionally be provided as a third list entry.
 
 For example,
 
 .. code-block:: llvm
 
-    ; A root scope (which doubles as a list of itself):
+    ; Two scope domains:
     !0 = metadata !{metadata !0}
+    !1 = metadata !{metadata !1}
 
-    ; Two child scopes (which must be self-referential to avoid being "uniqued"):
-    !1 = metadata !{metadata !2} ; A list containing only scope !2
-    !2 = metadata !{metadata !2, metadata !0} ; Scope !2 is a descendant of scope !0
+    ; Some scopes in these domains:
+    !2 = metadata !{metadata !2, metadata !0}
+    !3 = metadata !{metadata !3, metadata !0}
+    !4 = metadata !{metadata !4, metadata !1}
 
-    !3 = metadata !{metadata !4} ; A list containing only scope !4
-    !4 = metadata !{metadata !4, metadata !0} ; Scope !4 is a descendant of scope !0
+    ; Some scope lists:
+    !5 = metadata !{metadata !4} ; A list containing only scope !4
+    !6 = metadata !{metadata !4, metadata !3, metadata !2}
+    !7 = metadata !{metadata !3}
 
     ; These two instructions don't alias:
-    %0 = load float* %c, align 4, !alias.scope !0
-    store float %0, float* %arrayidx.i, align 4, !noalias !0
-
-    ; These two instructions may alias (scope !2 and scope !4 are peers):
-    %2 = load float* %c, align 4, !alias.scope !1
-    store float %2, float* %arrayidx.i2, align 4, !noalias !3
-
-    ; These two instructions don't alias (scope !2 is a descendant of scope !0
-    ; and the store does not alias with anything in scope !0 or any of its descendants):
-    %2 = load float* %c, align 4, !alias.scope !1
-    store float %0, float* %arrayidx.i, align 4, !noalias !0
-
-    ; These two instructions may alias:
-    %2 = load float* %c, align 4, !alias.scope !0
-    store float %0, float* %arrayidx.i, align 4, !noalias !1
+    %0 = load float* %c, align 4, !alias.scope !5
+    store float %0, float* %arrayidx.i, align 4, !noalias !5
+
+    ; These two instructions also don't alias (for domain !1, the set of scopes
+    ; in the !alias.scope equals that in the !noalias list):
+    %2 = load float* %c, align 4, !alias.scope !5
+    store float %2, float* %arrayidx.i2, align 4, !noalias !6
+
+    ; These two instructions don't alias (for domain !0, the set of scopes in
+    ; the !noalias list is not a superset of, or equal to, the scopes in the
+    ; !alias.scope list):
+    %2 = load float* %c, align 4, !alias.scope !6
+    store float %0, float* %arrayidx.i, align 4, !noalias !7
 
 '``fpmath``' Metadata
 ^^^^^^^^^^^^^^^^^^^^^
index 6672b14f59ac46f062c947e98e059c84841ec1e1..d29512ce1fffe0f6fe2368e1221410782b5e8d6c 100644 (file)
@@ -70,7 +70,8 @@ protected:
   /// \brief Return metadata appropriate for a AA root node (scope or TBAA).
   /// Each returned node is distinct from all other metadata and will never
   /// be identified (uniqued) with anything else.
-  MDNode *createAnonymousAARoot(StringRef Name = StringRef());
+  MDNode *createAnonymousAARoot(StringRef Name = StringRef(),
+                                MDNode *Extra = nullptr);
 
 public:
   /// \brief Return metadata appropriate for a TBAA root node. Each returned
@@ -80,32 +81,41 @@ public:
     return createAnonymousAARoot();
   }
 
-  /// \brief Return metadata appropriate for an alias scope root node.
+  /// \brief Return metadata appropriate for an alias scope domain node.
   /// Each returned node is distinct from all other metadata and will never
   /// be identified (uniqued) with anything else.
-  MDNode *createAnonymousAliasScopeRoot(StringRef Name = StringRef()) {
+  MDNode *createAnonymousAliasScopeDomain(StringRef Name = StringRef()) {
     return createAnonymousAARoot(Name);
   }
 
+  /// \brief Return metadata appropriate for an alias scope root node.
+  /// Each returned node is distinct from all other metadata and will never
+  /// be identified (uniqued) with anything else.
+  MDNode *createAnonymousAliasScope(MDNode *Domain,
+                                    StringRef Name = StringRef()) {
+    return createAnonymousAARoot(Name, Domain);
+  }
+
   /// \brief Return metadata appropriate for a TBAA root node with the given
   /// name.  This may be identified (uniqued) with other roots with the same
   /// name.
   MDNode *createTBAARoot(StringRef Name);
 
-  /// \brief Return metadata appropriate for an alias scope root node with
+  /// \brief Return metadata appropriate for an alias scope domain node with
   /// the given name. This may be identified (uniqued) with other roots with
   /// the same name.
-  MDNode *createAliasScopeRoot(StringRef Name);
+  MDNode *createAliasScopeDomain(StringRef Name);
+
+  /// \brief Return metadata appropriate for an alias scope node with
+  /// the given name. This may be identified (uniqued) with other scopes with
+  /// the same name and domain.
+  MDNode *createAliasScope(StringRef Name, MDNode *Domain);
 
   /// \brief Return metadata for a non-root TBAA node with the given name,
   /// parent in the TBAA tree, and value for 'pointsToConstantMemory'.
   MDNode *createTBAANode(StringRef Name, MDNode *Parent,
                          bool isConstant = false);
 
-  /// \brief Return metadata for a non-root alias scope node with the given
-  /// name and parent in the scope tree.
-  MDNode *createAliasScopeNode(StringRef Name, MDNode *Parent);
-
   struct TBAAStructField {
     uint64_t Offset;
     uint64_t Size;
index f197f1c48a6cb9de183a321de035fa186206d946..d090227d3184d439a55f09ebc5722955853b02aa 100644 (file)
 // This file defines the ScopedNoAlias alias-analysis pass, which implements
 // metadata-based scoped no-alias support.
 //
-// Alias-analysis scopes are defined similar to TBAA nodes:
+// Alias-analysis scopes are defined by an id (which can be a string or some
+// other metadata node), a domain node, and an optional descriptive string.
+// A domain is defined by an id (which can be a string or some other metadata
+// node), and an optional descriptive string.
 //
-// !scope0 = metadata !{ metadata !"scope of foo()" }
-// !scope1 = metadata !{ metadata !"scope 1", metadata !scope0 }
-// !scope2 = metadata !{ metadata !"scope 2", metadata !scope0 }
-// !scope3 = metadata !{ metadata !"scope 2.1", metadata !scope2 }
-// !scope4 = metadata !{ metadata !"scope 2.2", metadata !scope2 }
+// !dom0 =   metadata !{ metadata !"domain of foo()" }
+// !scope1 = metadata !{ metadata !scope1, metadata !dom0, metadata !"scope 1" }
+// !scope2 = metadata !{ metadata !scope2, metadata !dom0, metadata !"scope 2" }
 //
 // Loads and stores can be tagged with an alias-analysis scope, and also, with
 // a noalias tag for a specific scope:
 // ... = load %ptr2, !alias.scope !{ !scope1, !scope2 }, !noalias !{ !scope1 }
 //
 // When evaluating an aliasing query, if one of the instructions is associated
-// with an alias.scope id that is identical to the noalias scope associated
-// with the other instruction, or is a descendant (in the scope hierarchy) of
-// the noalias scope associated with the other instruction, then the two memory
+// has a set of noalias scopes in some domain that is superset of the alias
+// scopes in that domain of some other instruction, then the two memory
 // accesses are assumed not to alias.
 //
-// Note that if the first element of the scope metadata is a string, then it
-// can be combined accross functions and translation units. The string can be
-// replaced by a self-reference to create globally unqiue scope identifiers.
-//
 //===----------------------------------------------------------------------===//
 
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/Analysis/Passes.h"
 #include "llvm/Analysis/AliasAnalysis.h"
 #include "llvm/IR/Constants.h"
@@ -66,15 +63,11 @@ public:
   /// getNode - Get the MDNode for this AliasScopeNode.
   const MDNode *getNode() const { return Node; }
 
-  /// getParent - Get this AliasScopeNode's Alias tree parent.
-  AliasScopeNode getParent() const {
+  /// getDomain - Get the MDNode for this AliasScopeNode's domain.
+  const MDNode *getDomain() const {
     if (Node->getNumOperands() < 2)
-      return AliasScopeNode();
-    MDNode *P = dyn_cast_or_null<MDNode>(Node->getOperand(1));
-    if (!P)
-      return AliasScopeNode();
-    // Ok, this node has a valid parent. Return it.
-    return AliasScopeNode(P);
+      return nullptr;
+    return dyn_cast_or_null<MDNode>(Node->getOperand(1));
   }
 };
 
@@ -102,8 +95,9 @@ public:
   }
 
 protected:
-  bool mayAlias(const MDNode *A, const MDNode *B) const;
   bool mayAliasInScopes(const MDNode *Scopes, const MDNode *NoAlias) const;
+  void collectMDInDomain(const MDNode *List, const MDNode *Domain,
+                         SmallPtrSetImpl<const MDNode *> &Nodes) const;
 
 private:
   virtual void getAnalysisUsage(AnalysisUsage &AU) const;
@@ -133,24 +127,13 @@ ScopedNoAliasAA::getAnalysisUsage(AnalysisUsage &AU) const {
   AliasAnalysis::getAnalysisUsage(AU);
 }
 
-/// mayAlias - Test whether the scope represented by A may alias the
-/// scope represented by B. Specifically, A is the target scope, and B is the
-/// noalias scope.
-bool
-ScopedNoAliasAA::mayAlias(const MDNode *A,
-                          const MDNode *B) const {
-  // Climb the tree from A to see if we reach B.
-  for (AliasScopeNode T(A); ; ) {
-    if (T.getNode() == B)
-      // B is an ancestor of A.
-      return false;
-
-    T = T.getParent();
-    if (!T.getNode())
-      break;
-  }
-
-  return true;
+void
+ScopedNoAliasAA::collectMDInDomain(const MDNode *List, const MDNode *Domain,
+                   SmallPtrSetImpl<const MDNode *> &Nodes) const {
+  for (unsigned i = 0, ie = List->getNumOperands(); i != ie; ++i)
+    if (const MDNode *MD = dyn_cast<MDNode>(List->getOperand(i)))
+      if (AliasScopeNode(MD).getDomain() == Domain)
+        Nodes.insert(MD);
 }
 
 bool
@@ -159,14 +142,35 @@ ScopedNoAliasAA::mayAliasInScopes(const MDNode *Scopes,
   if (!Scopes || !NoAlias)
     return true;
 
-  for (unsigned i = 0, ie = Scopes->getNumOperands(); i != ie; ++i)
-    if (const MDNode *SMD = dyn_cast<MDNode>(Scopes->getOperand(i)))
-      for (unsigned j = 0, je = NoAlias->getNumOperands(); j != je; ++j)
-        if (const MDNode *NAMD = dyn_cast<MDNode>(NoAlias->getOperand(j)))
-          if (!mayAlias(SMD, NAMD))
-            return false;
+  // Collect the set of scope domains relevant to the noalias scopes.
+  SmallPtrSet<const MDNode *, 16> Domains;
+  for (unsigned i = 0, ie = NoAlias->getNumOperands(); i != ie; ++i)
+    if (const MDNode *NAMD = dyn_cast<MDNode>(NoAlias->getOperand(i)))
+      if (const MDNode *Domain = AliasScopeNode(NAMD).getDomain())
+        Domains.insert(Domain);
+
+  // We alias unless, for some domain, the set of noalias scopes in that domain
+  // is a superset of the set of alias scopes in that domain.
+  for (const MDNode *Domain : Domains) {
+    SmallPtrSet<const MDNode *, 16> NANodes, ScopeNodes;
+    collectMDInDomain(NoAlias, Domain, NANodes);
+    collectMDInDomain(Scopes, Domain, ScopeNodes);
+    if (!ScopeNodes.size())
+      continue;
+
+    // To not alias, all of the nodes in ScopeNodes must be in NANodes.
+    bool FoundAll = true;
+    for (const MDNode *SMD : ScopeNodes)
+      if (!NANodes.count(SMD)) {
+        FoundAll = false;
+        break;
+      }
+
+    if (FoundAll)
+      return false;
+  }
 
-  return true; 
+  return true;
 }
 
 AliasAnalysis::AliasResult
index 103915f5ae534576b4bb644bda15c937a2304102..39307a26f244bc7399d8a7d2ace0f9d14f9a5430 100644 (file)
@@ -60,11 +60,13 @@ MDNode *MDBuilder::createRange(const APInt &Lo, const APInt &Hi) {
   return MDNode::get(Context, Range);
 }
 
-MDNode *MDBuilder::createAnonymousAARoot(StringRef Name) {
+MDNode *MDBuilder::createAnonymousAARoot(StringRef Name, MDNode *Extra) {
   // To ensure uniqueness the root node is self-referential.
   MDNode *Dummy = MDNode::getTemporary(Context, ArrayRef<Value*>());
 
-  SmallVector<Value *, 2> Args(1, Dummy);
+  SmallVector<Value *, 3> Args(1, Dummy);
+  if (Extra)
+    Args.push_back(Extra);
   if (!Name.empty())
     Args.push_back(createString(Name));
   MDNode *Root = MDNode::get(Context, Args);
@@ -98,12 +100,12 @@ MDNode *MDBuilder::createTBAANode(StringRef Name, MDNode *Parent,
   }
 }
 
-MDNode *MDBuilder::createAliasScopeRoot(StringRef Name) {
+MDNode *MDBuilder::createAliasScopeDomain(StringRef Name) {
   return MDNode::get(Context, createString(Name));
 }
 
-MDNode *MDBuilder::createAliasScopeNode(StringRef Name, MDNode *Parent) {
-  Value *Ops[2] = { createString(Name), Parent };
+MDNode *MDBuilder::createAliasScope(StringRef Name, MDNode *Domain) {
+  Value *Ops[2] = { createString(Name), Domain };
   return MDNode::get(Context, Ops);
 }
 
diff --git a/test/Analysis/ScopedNoAliasAA/basic-domains.ll b/test/Analysis/ScopedNoAliasAA/basic-domains.ll
new file mode 100644 (file)
index 0000000..d88a496
--- /dev/null
@@ -0,0 +1,57 @@
+; RUN: opt < %s -basicaa -scoped-noalias -aa-eval -evaluate-aa-metadata -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
+target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @foo1(float* nocapture %a, float* nocapture readonly %c) #0 {
+entry:
+; CHECK-LABEL: Function: foo1
+  %0 = load float* %c, align 4, !alias.scope !9
+  %arrayidx.i = getelementptr inbounds float* %a, i64 5
+  store float %0, float* %arrayidx.i, align 4, !noalias !6
+
+  %1 = load float* %c, align 4, !alias.scope !5
+  %arrayidx.i2 = getelementptr inbounds float* %a, i64 15
+  store float %1, float* %arrayidx.i2, align 4, !noalias !6
+
+  %2 = load float* %c, align 4, !alias.scope !6
+  %arrayidx.i3 = getelementptr inbounds float* %a, i64 16
+  store float %2, float* %arrayidx.i3, align 4, !noalias !5
+
+  ret void
+}
+
+attributes #0 = { nounwind uwtable }
+
+!0 = metadata !{metadata !0, metadata !"some domain"}
+!1 = metadata !{metadata !1, metadata !"some other domain"}
+
+; Two scopes (which must be self-referential to avoid being "uniqued"):
+!2 = metadata !{metadata !2, metadata !0, metadata !"a scope in dom0"}
+!3 = metadata !{metadata !2}
+
+!4 = metadata !{metadata !4, metadata !0, metadata !"another scope in dom0"}
+!5 = metadata !{metadata !4}
+
+; A list of the two scopes.
+!6 = metadata !{metadata !2, metadata !4}
+
+; Another scope in the second domain
+!7 = metadata !{metadata !7, metadata !1, metadata !"another scope in dom1"}
+!8 = metadata !{metadata !7}
+
+; A list of scopes from both domains.
+!9 = metadata !{metadata !2, metadata !4, metadata !7}
+
+; CHECK: NoAlias:   %0 = load float* %c, align 4, !alias.scope !0 <->   store float %0, float* %arrayidx.i, align 4, !noalias !6
+; CHECK: NoAlias:   %0 = load float* %c, align 4, !alias.scope !0 <->   store float %1, float* %arrayidx.i2, align 4, !noalias !6
+; CHECK: MayAlias:   %0 = load float* %c, align 4, !alias.scope !0 <->   store float %2, float* %arrayidx.i3, align 4, !noalias !7
+; CHECK: NoAlias:   %1 = load float* %c, align 4, !alias.scope !7 <->   store float %0, float* %arrayidx.i, align 4, !noalias !6
+; CHECK: NoAlias:   %1 = load float* %c, align 4, !alias.scope !7 <->   store float %1, float* %arrayidx.i2, align 4, !noalias !6
+; CHECK: NoAlias:   %1 = load float* %c, align 4, !alias.scope !7 <->   store float %2, float* %arrayidx.i3, align 4, !noalias !7
+; CHECK: NoAlias:   %2 = load float* %c, align 4, !alias.scope !6 <->   store float %0, float* %arrayidx.i, align 4, !noalias !6
+; CHECK: NoAlias:   %2 = load float* %c, align 4, !alias.scope !6 <->   store float %1, float* %arrayidx.i2, align 4, !noalias !6
+; CHECK: MayAlias:   %2 = load float* %c, align 4, !alias.scope !6 <->   store float %2, float* %arrayidx.i3, align 4, !noalias !7
+; CHECK: NoAlias:   store float %1, float* %arrayidx.i2, align 4, !noalias !6 <->   store float %0, float* %arrayidx.i, align 4, !noalias !6
+; CHECK: NoAlias:   store float %2, float* %arrayidx.i3, align 4, !noalias !7 <->   store float %0, float* %arrayidx.i, align 4, !noalias !6
+; CHECK: NoAlias:   store float %2, float* %arrayidx.i3, align 4, !noalias !7 <->   store float %1, float* %arrayidx.i2, align 4, !noalias !6
+
index bae977f28ee5e0b3ef777097e2cdb659c2347f86..73fe33390382944576146190ff504a41cc53bd97 100644 (file)
@@ -5,9 +5,9 @@ target triple = "x86_64-unknown-linux-gnu"
 define void @foo1(float* nocapture %a, float* nocapture readonly %c) #0 {
 entry:
 ; CHECK-LABEL: Function: foo1
-  %0 = load float* %c, align 4, !alias.scope !0
+  %0 = load float* %c, align 4, !alias.scope !1
   %arrayidx.i = getelementptr inbounds float* %a, i64 5
-  store float %0, float* %arrayidx.i, align 4, !noalias !0
+  store float %0, float* %arrayidx.i, align 4, !noalias !1
   %1 = load float* %c, align 4
   %arrayidx = getelementptr inbounds float* %a, i64 7
   store float %1, float* %arrayidx, align 4
@@ -22,5 +22,6 @@ entry:
 
 attributes #0 = { nounwind uwtable }
 
-!0 = metadata !{metadata !0}
+!0 = metadata !{metadata !0, metadata !"some domain"}
+!1 = metadata !{metadata !1, metadata !0, metadata !"some scope"}
 
index 44393bbdaabbb7191a14f6f6e5f4a103c50594a3..37b0add68bd119a578c0aacbcd3b611f95685083 100644 (file)
@@ -7,28 +7,35 @@ entry:
 ; CHECK-LABEL: Function: foo2
   %0 = load float* %c, align 4, !alias.scope !0
   %arrayidx.i = getelementptr inbounds float* %a, i64 5
-  store float %0, float* %arrayidx.i, align 4, !alias.scope !2, !noalias !1
+  store float %0, float* %arrayidx.i, align 4, !alias.scope !5, !noalias !4
   %arrayidx1.i = getelementptr inbounds float* %b, i64 8
-  store float %0, float* %arrayidx1.i, align 4, !alias.scope !1, !noalias !2
+  store float %0, float* %arrayidx1.i, align 4, !alias.scope !0, !noalias !5
   %1 = load float* %c, align 4
   %arrayidx = getelementptr inbounds float* %a, i64 7
   store float %1, float* %arrayidx, align 4
   ret void
 
-; CHECK: NoAlias:   %0 = load float* %c, align 4, !alias.scope !0 <->   store float %0, float* %arrayidx.i, align 4, !alias.scope !2, !noalias !1
-; CHECK: NoAlias:   %0 = load float* %c, align 4, !alias.scope !0 <->   store float %0, float* %arrayidx1.i, align 4, !alias.scope !1, !noalias !2
+; CHECK: MayAlias:   %0 = load float* %c, align 4, !alias.scope !0 <->   store float %0, float* %arrayidx.i, align 4, !alias.scope !4, !noalia
+; CHECK: s !5
+; CHECK: MayAlias:   %0 = load float* %c, align 4, !alias.scope !0 <->   store float %0, float* %arrayidx1.i, align 4, !alias.scope !0, !noali
+; CHECK: as !4
 ; CHECK: MayAlias:   %0 = load float* %c, align 4, !alias.scope !0 <->   store float %1, float* %arrayidx, align 4
-; CHECK: MayAlias:   %1 = load float* %c, align 4 <->   store float %0, float* %arrayidx.i, align 4, !alias.scope !2, !noalias !1
-; CHECK: MayAlias:   %1 = load float* %c, align 4 <->   store float %0, float* %arrayidx1.i, align 4, !alias.scope !1, !noalias !2
+; CHECK: MayAlias:   %1 = load float* %c, align 4 <->   store float %0, float* %arrayidx.i, align 4, !alias.scope !4, !noalias !5
+; CHECK: MayAlias:   %1 = load float* %c, align 4 <->   store float %0, float* %arrayidx1.i, align 4, !alias.scope !0, !noalias !4
 ; CHECK: MayAlias:   %1 = load float* %c, align 4 <->   store float %1, float* %arrayidx, align 4
-; CHECK: NoAlias:   store float %0, float* %arrayidx1.i, align 4, !alias.scope !1, !noalias !2 <->   store float %0, float* %arrayidx.i, align 4, !alias.scope !2, !noalias !1
-; CHECK: NoAlias:   store float %1, float* %arrayidx, align 4 <->   store float %0, float* %arrayidx.i, align 4, !alias.scope !2, !noalias !1
-; CHECK: MayAlias:   store float %1, float* %arrayidx, align 4 <->   store float %0, float* %arrayidx1.i, align 4, !alias.scope !1, !noalias !2
+; CHECK: NoAlias:   store float %0, float* %arrayidx1.i, align 4, !alias.scope !0, !noalias !4 <->   store float %0, float* %arrayidx.i, align
+; CHECK: 4, !alias.scope !4, !noalias !5
+; CHECK: NoAlias:   store float %1, float* %arrayidx, align 4 <->   store float %0, float* %arrayidx.i, align 4, !alias.scope !4, !noalias !5
+; CHECK: MayAlias:   store float %1, float* %arrayidx, align 4 <->   store float %0, float* %arrayidx1.i, align 4, !alias.scope !0, !noalias !
+; CHECK: 4
 }
 
 attributes #0 = { nounwind uwtable }
 
-!0 = metadata !{metadata !1, metadata !2}
-!1 = metadata !{metadata !1}
-!2 = metadata !{metadata !2}
+!0 = metadata !{metadata !1, metadata !3}
+!1 = metadata !{metadata !1, metadata !2, metadata !"some scope"}
+!2 = metadata !{metadata !2, metadata !"some domain"}
+!3 = metadata !{metadata !3, metadata !2, metadata !"some other scope"}
+!4 = metadata !{metadata !1}
+!5 = metadata !{metadata !3}