diff --git a/docs/LangRef.rst b/docs/LangRef.rst index ddbf4003494..891b9c0af70 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -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. +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 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 +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 + %0 = load float* %c, align 4, !alias.scope !5 + store float %0, float* %arrayidx.i, align 4, !noalias !5 - ; 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 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 (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 + ; 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 ^^^^^^^^^^^^^^^^^^^^^ diff --git a/include/llvm/IR/MDBuilder.h b/include/llvm/IR/MDBuilder.h index 6672b14f59a..d29512ce1ff 100644 --- a/include/llvm/IR/MDBuilder.h +++ b/include/llvm/IR/MDBuilder.h @@ -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,11 +81,19 @@ public: return createAnonymousAARoot(); } + /// \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 *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 *createAnonymousAliasScopeRoot(StringRef Name = StringRef()) { - return createAnonymousAARoot(Name); + MDNode *createAnonymousAliasScope(MDNode *Domain, + StringRef Name = StringRef()) { + return createAnonymousAARoot(Name, Domain); } /// \brief Return metadata appropriate for a TBAA root node with the given @@ -92,20 +101,21 @@ public: /// 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; diff --git a/lib/Analysis/ScopedNoAliasAA.cpp b/lib/Analysis/ScopedNoAliasAA.cpp index f197f1c48a6..d090227d318 100644 --- a/lib/Analysis/ScopedNoAliasAA.cpp +++ b/lib/Analysis/ScopedNoAliasAA.cpp @@ -10,13 +10,14 @@ // 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: @@ -25,17 +26,13 @@ // ... = 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(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(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 &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 &Nodes) const { + for (unsigned i = 0, ie = List->getNumOperands(); i != ie; ++i) + if (const MDNode *MD = dyn_cast(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(Scopes->getOperand(i))) - for (unsigned j = 0, je = NoAlias->getNumOperands(); j != je; ++j) - if (const MDNode *NAMD = dyn_cast(NoAlias->getOperand(j))) - if (!mayAlias(SMD, NAMD)) - return false; + // Collect the set of scope domains relevant to the noalias scopes. + SmallPtrSet Domains; + for (unsigned i = 0, ie = NoAlias->getNumOperands(); i != ie; ++i) + if (const MDNode *NAMD = dyn_cast(NoAlias->getOperand(i))) + if (const MDNode *Domain = AliasScopeNode(NAMD).getDomain()) + Domains.insert(Domain); - return true; + // 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 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; } AliasAnalysis::AliasResult diff --git a/lib/IR/MDBuilder.cpp b/lib/IR/MDBuilder.cpp index 103915f5ae5..39307a26f24 100644 --- a/lib/IR/MDBuilder.cpp +++ b/lib/IR/MDBuilder.cpp @@ -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()); - SmallVector Args(1, Dummy); + SmallVector 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 index 00000000000..d88a4966c41 --- /dev/null +++ b/test/Analysis/ScopedNoAliasAA/basic-domains.ll @@ -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 + diff --git a/test/Analysis/ScopedNoAliasAA/basic.ll b/test/Analysis/ScopedNoAliasAA/basic.ll index bae977f28ee..73fe3339038 100644 --- a/test/Analysis/ScopedNoAliasAA/basic.ll +++ b/test/Analysis/ScopedNoAliasAA/basic.ll @@ -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"} diff --git a/test/Analysis/ScopedNoAliasAA/basic2.ll b/test/Analysis/ScopedNoAliasAA/basic2.ll index 44393bbdaab..37b0add68bd 100644 --- a/test/Analysis/ScopedNoAliasAA/basic2.ll +++ b/test/Analysis/ScopedNoAliasAA/basic2.ll @@ -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}