#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CallGraph.h"
+#include "llvm/Analysis/CaptureTracking.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/Transforms/Utils/Local.h"
+#include "llvm/Support/CommandLine.h"
+#include <algorithm>
using namespace llvm;
+static cl::opt<bool>
+EnableNoAliasConversion("enable-noalias-to-md-conversion", cl::init(false),
+ cl::Hidden,
+ cl::desc("Convert noalias attributes to metadata during inlining."));
+
bool llvm::InlineFunction(CallInst *CI, InlineFunctionInfo &IFI,
bool InsertLifetime) {
return InlineFunction(CallSite(CI), IFI, InsertLifetime);
MDNode::deleteTemporary(DummyNodes[i]);
}
+/// AddAliasScopeMetadata - If the inlined function has noalias arguments, then
+/// add new alias scopes for each noalias argument, tag the mapped noalias
+/// parameters with noalias metadata specifying the new scope, and tag all
+/// non-derived loads, stores and memory intrinsics with the new alias scopes.
+static void AddAliasScopeMetadata(CallSite CS, ValueToValueMapTy &VMap,
+ const DataLayout *DL) {
+ if (!EnableNoAliasConversion)
+ return;
+
+ const Function *CalledFunc = CS.getCalledFunction();
+ SmallVector<const Argument *, 4> NoAliasArgs;
+
+ for (Function::const_arg_iterator I = CalledFunc->arg_begin(),
+ E = CalledFunc->arg_end(); I != E; ++I) {
+ if (I->hasNoAliasAttr() && !I->hasNUses(0))
+ NoAliasArgs.push_back(I);
+ }
+
+ if (NoAliasArgs.empty())
+ return;
+
+ // To do a good job, if a noalias variable is captured, we need to know if
+ // the capture point dominates the particular use we're considering.
+ DominatorTree DT;
+ DT.recalculate(const_cast<Function&>(*CalledFunc));
+
+ // noalias indicates that pointer values based on the argument do not alias
+ // pointer values which are not based on it. So we add a new "scope" for each
+ // noalias function argument. Accesses using pointers based on that argument
+ // become part of that alias scope, accesses using pointers not based on that
+ // argument are tagged as noalias with that scope.
+
+ DenseMap<const Argument *, MDNode *> NewScopes;
+ MDBuilder MDB(CalledFunc->getContext());
+
+ // Create a new scope domain for this function.
+ MDNode *NewDomain =
+ MDB.createAnonymousAliasScopeDomain(CalledFunc->getName());
+ for (unsigned i = 0, e = NoAliasArgs.size(); i != e; ++i) {
+ const Argument *A = NoAliasArgs[i];
+
+ std::string Name = CalledFunc->getName();
+ if (A->hasName()) {
+ Name += ": %";
+ Name += A->getName();
+ } else {
+ Name += ": argument ";
+ Name += utostr(i);
+ }
+
+ // Note: We always create a new anonymous root here. This is true regardless
+ // of the linkage of the callee because the aliasing "scope" is not just a
+ // property of the callee, but also all control dependencies in the caller.
+ MDNode *NewScope = MDB.createAnonymousAliasScope(NewDomain, Name);
+ NewScopes.insert(std::make_pair(A, NewScope));
+ }
+
+ // Iterate over all new instructions in the map; for all memory-access
+ // instructions, add the alias scope metadata.
+ for (ValueToValueMapTy::iterator VMI = VMap.begin(), VMIE = VMap.end();
+ VMI != VMIE; ++VMI) {
+ if (const Instruction *I = dyn_cast<Instruction>(VMI->first)) {
+ if (!VMI->second)
+ continue;
+
+ Instruction *NI = dyn_cast<Instruction>(VMI->second);
+ if (!NI)
+ continue;
+
+ SmallVector<const Value *, 2> PtrArgs;
+
+ if (const LoadInst *LI = dyn_cast<LoadInst>(I))
+ PtrArgs.push_back(LI->getPointerOperand());
+ else if (const StoreInst *SI = dyn_cast<StoreInst>(I))
+ PtrArgs.push_back(SI->getPointerOperand());
+ else if (const VAArgInst *VAAI = dyn_cast<VAArgInst>(I))
+ PtrArgs.push_back(VAAI->getPointerOperand());
+ else if (const AtomicCmpXchgInst *CXI = dyn_cast<AtomicCmpXchgInst>(I))
+ PtrArgs.push_back(CXI->getPointerOperand());
+ else if (const AtomicRMWInst *RMWI = dyn_cast<AtomicRMWInst>(I))
+ PtrArgs.push_back(RMWI->getPointerOperand());
+ else if (const MemIntrinsic *MI = dyn_cast<MemIntrinsic>(I)) {
+ PtrArgs.push_back(MI->getRawDest());
+ if (const MemTransferInst *MTI = dyn_cast<MemTransferInst>(MI))
+ PtrArgs.push_back(MTI->getRawSource());
+ }
+
+ // If we found no pointers, then this instruction is not suitable for
+ // pairing with an instruction to receive aliasing metadata.
+ // Simplification during cloning could make this happen, and skip these
+ // cases for now.
+ if (PtrArgs.empty())
+ continue;
+
+ // It is possible that there is only one underlying object, but you
+ // need to go through several PHIs to see it, and thus could be
+ // repeated in the Objects list.
+ SmallPtrSet<const Value *, 4> ObjSet;
+ SmallVector<Value *, 4> Scopes, NoAliases;
+
+ SmallSetVector<const Argument *, 4> NAPtrArgs;
+ for (unsigned i = 0, ie = PtrArgs.size(); i != ie; ++i) {
+ SmallVector<Value *, 4> Objects;
+ GetUnderlyingObjects(const_cast<Value*>(PtrArgs[i]),
+ Objects, DL, /* MaxLookup = */ 0);
+
+ for (Value *O : Objects)
+ ObjSet.insert(O);
+ }
+
+ // Figure out if we're derived from anyhing that is not a noalias
+ // argument.
+ bool CanDeriveViaCapture = false;
+ for (const Value *V : ObjSet)
+ if (!isIdentifiedFunctionLocal(const_cast<Value*>(V))) {
+ CanDeriveViaCapture = true;
+ break;
+ }
+
+ // First, we want to figure out all of the sets with which we definitely
+ // don't alias. Iterate over all noalias set, and add those for which:
+ // 1. The noalias argument is not in the set of objects from which we
+ // definitely derive.
+ // 2. The noalias argument has not yet been captured.
+ for (const Argument *A : NoAliasArgs) {
+ if (!ObjSet.count(A) && (!CanDeriveViaCapture ||
+ A->hasNoCaptureAttr() ||
+ !PointerMayBeCapturedBefore(A,
+ /* ReturnCaptures */ false,
+ /* StoreCaptures */ false, I, &DT)))
+ NoAliases.push_back(NewScopes[A]);
+ }
+
+ if (!NoAliases.empty())
+ NI->setMetadata(LLVMContext::MD_noalias, MDNode::concatenate(
+ NI->getMetadata(LLVMContext::MD_noalias),
+ MDNode::get(CalledFunc->getContext(), NoAliases)));
+ // Next, we want to figure out all of the sets to which we might belong.
+ // We might below to a set if:
+ // 1. The noalias argument is in the set of underlying objects
+ // or
+ // 2. There is some non-noalias argument in our list and the no-alias
+ // argument has been captured.
+
+ for (const Argument *A : NoAliasArgs) {
+ if (ObjSet.count(A) || (CanDeriveViaCapture &&
+ PointerMayBeCapturedBefore(A,
+ /* ReturnCaptures */ false,
+ /* StoreCaptures */ false,
+ I, &DT)))
+ Scopes.push_back(NewScopes[A]);
+ }
+
+ if (!Scopes.empty())
+ NI->setMetadata(LLVMContext::MD_alias_scope, MDNode::concatenate(
+ NI->getMetadata(LLVMContext::MD_alias_scope),
+ MDNode::get(CalledFunc->getContext(), Scopes)));
+ }
+ }
+}
+
/// UpdateCallGraphAfterInlining - Once we have cloned code over from a callee
/// into the caller, update the specified callgraph to reflect the changes we
/// made. Note that it's possible that not all code was copied over, so only
// Clone existing noalias metadata if necessary.
CloneAliasScopeMetadata(CS, VMap);
+
+ // Add noalias metadata if necessary.
+ AddAliasScopeMetadata(CS, VMap, IFI.DL);
}
// If there are any alloca instructions in the block that used to be the entry
+++ /dev/null
-; 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 !0
- %arrayidx.i = getelementptr inbounds float* %a, i64 5
- store float %0, float* %arrayidx.i, align 4, !noalias !0
-
- %1 = load float* %c, align 4
- %arrayidx = getelementptr inbounds float* %a, i64 7
- store float %1, float* %arrayidx, align 4
-
- %2 = load float* %c, align 4, !alias.scope !1
- %arrayidx.i2 = getelementptr inbounds float* %a, i64 15
- store float %2, float* %arrayidx.i2, align 4, !noalias !3
-
- %3 = load float* %c, align 4, !alias.scope !3
- %arrayidx.i3 = getelementptr inbounds float* %a, i64 16
- store float %3, float* %arrayidx.i3, align 4, !noalias !0
-
- %4 = load float* %c, align 4, !alias.scope !5
- %arrayidx.i4 = getelementptr inbounds float* %a, i64 17
- store float %4, float* %arrayidx.i4, align 4, !noalias !3
- ret void
-}
-
-attributes #0 = { nounwind uwtable }
-
-; A root scope (which doubles as a list of itself):
-!0 = metadata !{metadata !0}
-
-; Two child scopes (which must be self-referential to avoid being "uniqued"):
-!1 = metadata !{metadata !2}
-!2 = metadata !{metadata !2, metadata !0}
-
-!3 = metadata !{metadata !4}
-!4 = metadata !{metadata !4, metadata !0}
-
-; A list of the two children:
-!5 = metadata !{metadata !2, metadata !4}
-
-; CHECK: NoAlias: %0 = load float* %c, align 4, !alias.scope !0 <-> store float %0, float* %arrayidx.i, align 4, !noalias !0
-; CHECK: MayAlias: %0 = load float* %c, align 4, !alias.scope !0 <-> store float %1, float* %arrayidx, align 4
-; CHECK: MayAlias: %0 = load float* %c, align 4, !alias.scope !0 <-> store float %2, float* %arrayidx.i2, align 4, !noalias !3
-; CHECK: NoAlias: %0 = load float* %c, align 4, !alias.scope !0 <-> store float %3, float* %arrayidx.i3, align 4, !noalias !0
-; CHECK: MayAlias: %0 = load float* %c, align 4, !alias.scope !0 <-> store float %4, float* %arrayidx.i4, align 4, !noalias !3
-; CHECK: MayAlias: %1 = load float* %c, align 4 <-> store float %0, float* %arrayidx.i, align 4, !noalias !0
-; CHECK: MayAlias: %1 = load float* %c, align 4 <-> store float %1, float* %arrayidx, align 4
-; CHECK: MayAlias: %1 = load float* %c, align 4 <-> store float %2, float* %arrayidx.i2, align 4, !noalias !3
-; CHECK: MayAlias: %1 = load float* %c, align 4 <-> store float %3, float* %arrayidx.i3, align 4, !noalias !0
-; CHECK: MayAlias: %1 = load float* %c, align 4 <-> store float %4, float* %arrayidx.i4, align 4, !noalias !3
-; CHECK: NoAlias: %2 = load float* %c, align 4, !alias.scope !1 <-> store float %0, float* %arrayidx.i, align 4, !noalias !0
-; CHECK: MayAlias: %2 = load float* %c, align 4, !alias.scope !1 <-> store float %1, float* %arrayidx, align 4
-; CHECK: MayAlias: %2 = load float* %c, align 4, !alias.scope !1 <-> store float %2, float* %arrayidx.i2, align 4, !noalias !3
-; CHECK: NoAlias: %2 = load float* %c, align 4, !alias.scope !1 <-> store float %3, float* %arrayidx.i3, align 4, !noalias !0
-; CHECK: MayAlias: %2 = load float* %c, align 4, !alias.scope !1 <-> store float %4, float* %arrayidx.i4, align 4, !noalias !3
-; CHECK: NoAlias: %3 = load float* %c, align 4, !alias.scope !3 <-> store float %0, float* %arrayidx.i, align 4, !noalias !0
-; CHECK: MayAlias: %3 = load float* %c, align 4, !alias.scope !3 <-> store float %1, float* %arrayidx, align 4
-; CHECK: NoAlias: %3 = load float* %c, align 4, !alias.scope !3 <-> store float %2, float* %arrayidx.i2, align 4, !noalias !3
-; CHECK: NoAlias: %3 = load float* %c, align 4, !alias.scope !3 <-> store float %3, float* %arrayidx.i3, align 4, !noalias !0
-; CHECK: NoAlias: %3 = load float* %c, align 4, !alias.scope !3 <-> store float %4, float* %arrayidx.i4, align 4, !noalias !3
-; CHECK: NoAlias: %4 = load float* %c, align 4, !alias.scope !5 <-> store float %0, float* %arrayidx.i, align 4, !noalias !0
-; CHECK: MayAlias: %4 = load float* %c, align 4, !alias.scope !5 <-> store float %1, float* %arrayidx, align 4
-; CHECK: NoAlias: %4 = load float* %c, align 4, !alias.scope !5 <-> store float %2, float* %arrayidx.i2, align 4, !noalias !3
-; CHECK: NoAlias: %4 = load float* %c, align 4, !alias.scope !5 <-> store float %3, float* %arrayidx.i3, align 4, !noalias !0
-; CHECK: NoAlias: %4 = load float* %c, align 4, !alias.scope !5 <-> store float %4, float* %arrayidx.i4, align 4, !noalias !3
-; CHECK: NoAlias: store float %1, float* %arrayidx, align 4 <-> store float %0, float* %arrayidx.i, align 4, !noalias !0
-; CHECK: NoAlias: store float %2, float* %arrayidx.i2, align 4, !noalias !3 <-> store float %0, float* %arrayidx.i, align 4, !noalias !0
-; CHECK: NoAlias: store float %2, float* %arrayidx.i2, align 4, !noalias !3 <-> store float %1, float* %arrayidx, align 4
-; CHECK: NoAlias: store float %3, float* %arrayidx.i3, align 4, !noalias !0 <-> store float %0, float* %arrayidx.i, align 4, !noalias !0
-; CHECK: NoAlias: store float %3, float* %arrayidx.i3, align 4, !noalias !0 <-> store float %1, float* %arrayidx, align 4
-; CHECK: NoAlias: store float %3, float* %arrayidx.i3, align 4, !noalias !0 <-> store float %2, float* %arrayidx.i2, align 4, !noalias !3
-; CHECK: NoAlias: store float %4, float* %arrayidx.i4, align 4, !noalias !3 <-> store float %0, float* %arrayidx.i, align 4, !noalias !0
-; CHECK: NoAlias: store float %4, float* %arrayidx.i4, align 4, !noalias !3 <-> store float %1, float* %arrayidx, align 4
-; CHECK: NoAlias: store float %4, float* %arrayidx.i4, align 4, !noalias !3 <-> store float %2, float* %arrayidx.i2, align 4, !noalias !3
-; CHECK: NoAlias: store float %4, float* %arrayidx.i4, align 4, !noalias !3 <-> store float %3, float* %arrayidx.i3, align 4, !noalias !0
-
--- /dev/null
+; RUN: opt -inline -enable-noalias-to-md-conversion -S < %s | 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 @hello(float* noalias nocapture %a, float* nocapture readonly %c) #0 {
+entry:
+ %0 = load float* %c, align 4
+ %arrayidx = getelementptr inbounds float* %a, i64 5
+ store float %0, float* %arrayidx, align 4
+ ret void
+}
+
+define void @foo(float* nocapture %a, float* nocapture readonly %c) #0 {
+entry:
+ tail call void @hello(float* %a, float* %c)
+ %0 = load float* %c, align 4
+ %arrayidx = getelementptr inbounds float* %a, i64 7
+ store float %0, float* %arrayidx, align 4
+ ret void
+}
+
+; CHECK: define void @foo(float* nocapture %a, float* nocapture readonly %c) #0 {
+; CHECK: entry:
+; CHECK: %0 = load float* %c, align 4, !noalias !0
+; CHECK: %arrayidx.i = getelementptr inbounds float* %a, i64 5
+; CHECK: store float %0, float* %arrayidx.i, align 4, !alias.scope !0
+; CHECK: %1 = load float* %c, align 4
+; CHECK: %arrayidx = getelementptr inbounds float* %a, i64 7
+; CHECK: store float %1, float* %arrayidx, align 4
+; CHECK: ret void
+; CHECK: }
+
+define void @hello2(float* noalias nocapture %a, float* noalias nocapture %b, float* nocapture readonly %c) #0 {
+entry:
+ %0 = load float* %c, align 4
+ %arrayidx = getelementptr inbounds float* %a, i64 5
+ store float %0, float* %arrayidx, align 4
+ %arrayidx1 = getelementptr inbounds float* %b, i64 8
+ store float %0, float* %arrayidx1, align 4
+ ret void
+}
+
+define void @foo2(float* nocapture %a, float* nocapture %b, float* nocapture readonly %c) #0 {
+entry:
+ tail call void @hello2(float* %a, float* %b, float* %c)
+ %0 = load float* %c, align 4
+ %arrayidx = getelementptr inbounds float* %a, i64 7
+ store float %0, float* %arrayidx, align 4
+ ret void
+}
+
+; CHECK: define void @foo2(float* nocapture %a, float* nocapture %b, float* nocapture readonly %c) #0 {
+; CHECK: entry:
+; CHECK: %0 = load float* %c, align 4, !noalias !3
+; CHECK: %arrayidx.i = getelementptr inbounds float* %a, i64 5
+; CHECK: store float %0, float* %arrayidx.i, align 4, !alias.scope !7, !noalias !8
+; CHECK: %arrayidx1.i = getelementptr inbounds float* %b, i64 8
+; CHECK: store float %0, float* %arrayidx1.i, align 4, !alias.scope !8, !noalias !7
+; CHECK: %1 = load float* %c, align 4
+; CHECK: %arrayidx = getelementptr inbounds float* %a, i64 7
+; CHECK: store float %1, float* %arrayidx, align 4
+; CHECK: ret void
+; CHECK: }
+
+attributes #0 = { nounwind uwtable }
+
+; CHECK: !0 = metadata !{metadata !1}
+; CHECK: !1 = metadata !{metadata !1, metadata !2, metadata !"hello: %a"}
+; CHECK: !2 = metadata !{metadata !2, metadata !"hello"}
+; CHECK: !3 = metadata !{metadata !4, metadata !6}
+; CHECK: !4 = metadata !{metadata !4, metadata !5, metadata !"hello2: %a"}
+; CHECK: !5 = metadata !{metadata !5, metadata !"hello2"}
+; CHECK: !6 = metadata !{metadata !6, metadata !5, metadata !"hello2: %b"}
+; CHECK: !7 = metadata !{metadata !4}
+; CHECK: !8 = metadata !{metadata !6}
+
--- /dev/null
+; RUN: opt -inline -enable-noalias-to-md-conversion -S < %s | 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 @hello(float* noalias nocapture %a, float* noalias nocapture readonly %c) #0 {
+entry:
+ %0 = load float* %c, align 4
+ %arrayidx = getelementptr inbounds float* %a, i64 5
+ store float %0, float* %arrayidx, align 4
+ ret void
+}
+
+define void @foo(float* noalias nocapture %a, float* noalias nocapture readonly %c) #0 {
+entry:
+ tail call void @hello(float* %a, float* %c)
+ %0 = load float* %c, align 4
+ %arrayidx = getelementptr inbounds float* %a, i64 7
+ store float %0, float* %arrayidx, align 4
+ ret void
+}
+
+; CHECK: define void @foo(float* noalias nocapture %a, float* noalias nocapture readonly %c) #0 {
+; CHECK: entry:
+; CHECK: %0 = load float* %c, align 4, !alias.scope !0, !noalias !3
+; CHECK: %arrayidx.i = getelementptr inbounds float* %a, i64 5
+; CHECK: store float %0, float* %arrayidx.i, align 4, !alias.scope !3, !noalias !0
+; CHECK: %1 = load float* %c, align 4
+; CHECK: %arrayidx = getelementptr inbounds float* %a, i64 7
+; CHECK: store float %1, float* %arrayidx, align 4
+; CHECK: ret void
+; CHECK: }
+
+define void @hello2(float* noalias nocapture %a, float* noalias nocapture %b, float* nocapture readonly %c) #0 {
+entry:
+ %0 = load float* %c, align 4
+ %arrayidx = getelementptr inbounds float* %a, i64 6
+ store float %0, float* %arrayidx, align 4
+ %arrayidx1 = getelementptr inbounds float* %b, i64 8
+ store float %0, float* %arrayidx1, align 4
+ ret void
+}
+
+; Check that when hello() is inlined into foo(), and then foo() is inlined into
+; foo2(), the noalias scopes are properly concatenated.
+define void @foo2(float* nocapture %a, float* nocapture %b, float* nocapture readonly %c) #0 {
+entry:
+ tail call void @foo(float* %a, float* %c)
+ tail call void @hello2(float* %a, float* %b, float* %c)
+ %0 = load float* %c, align 4
+ %arrayidx = getelementptr inbounds float* %a, i64 7
+ store float %0, float* %arrayidx, align 4
+ ret void
+}
+
+; CHECK: define void @foo2(float* nocapture %a, float* nocapture %b, float* nocapture readonly %c) #0 {
+; CHECK: entry:
+; CHECK: %0 = load float* %c, align 4, !alias.scope !5, !noalias !10
+; CHECK: %arrayidx.i.i = getelementptr inbounds float* %a, i64 5
+; CHECK: store float %0, float* %arrayidx.i.i, align 4, !alias.scope !10, !noalias !5
+; CHECK: %1 = load float* %c, align 4, !alias.scope !13, !noalias !14
+; CHECK: %arrayidx.i = getelementptr inbounds float* %a, i64 7
+; CHECK: store float %1, float* %arrayidx.i, align 4, !alias.scope !14, !noalias !13
+; CHECK: %2 = load float* %c, align 4, !noalias !15
+; CHECK: %arrayidx.i1 = getelementptr inbounds float* %a, i64 6
+; CHECK: store float %2, float* %arrayidx.i1, align 4, !alias.scope !19, !noalias !20
+; CHECK: %arrayidx1.i = getelementptr inbounds float* %b, i64 8
+; CHECK: store float %2, float* %arrayidx1.i, align 4, !alias.scope !20, !noalias !19
+; CHECK: %3 = load float* %c, align 4
+; CHECK: %arrayidx = getelementptr inbounds float* %a, i64 7
+; CHECK: store float %3, float* %arrayidx, align 4
+; CHECK: ret void
+; CHECK: }
+
+; CHECK: !0 = metadata !{metadata !1}
+; CHECK: !1 = metadata !{metadata !1, metadata !2, metadata !"hello: %c"}
+; CHECK: !2 = metadata !{metadata !2, metadata !"hello"}
+; CHECK: !3 = metadata !{metadata !4}
+; CHECK: !4 = metadata !{metadata !4, metadata !2, metadata !"hello: %a"}
+; CHECK: !5 = metadata !{metadata !6, metadata !8}
+; CHECK: !6 = metadata !{metadata !6, metadata !7, metadata !"hello: %c"}
+; CHECK: !7 = metadata !{metadata !7, metadata !"hello"}
+; CHECK: !8 = metadata !{metadata !8, metadata !9, metadata !"foo: %c"}
+; CHECK: !9 = metadata !{metadata !9, metadata !"foo"}
+; CHECK: !10 = metadata !{metadata !11, metadata !12}
+; CHECK: !11 = metadata !{metadata !11, metadata !7, metadata !"hello: %a"}
+; CHECK: !12 = metadata !{metadata !12, metadata !9, metadata !"foo: %a"}
+; CHECK: !13 = metadata !{metadata !8}
+; CHECK: !14 = metadata !{metadata !12}
+; CHECK: !15 = metadata !{metadata !16, metadata !18}
+; CHECK: !16 = metadata !{metadata !16, metadata !17, metadata !"hello2: %a"}
+; CHECK: !17 = metadata !{metadata !17, metadata !"hello2"}
+; CHECK: !18 = metadata !{metadata !18, metadata !17, metadata !"hello2: %b"}
+; CHECK: !19 = metadata !{metadata !16}
+; CHECK: !20 = metadata !{metadata !18}
+
+attributes #0 = { nounwind uwtable }
+