diff --git a/clang/test/Driver/memtag_lto.c b/clang/test/Driver/memtag_lto.c --- a/clang/test/Driver/memtag_lto.c +++ b/clang/test/Driver/memtag_lto.c @@ -85,7 +85,7 @@ // RUN: -r %t.ltonewpm2.bc,use,plx \ // RUN: -r %t.ltonewpm2.bc,z, 2>&1 | FileCheck %s -check-prefixes=SSI,XSAFE,YSAFE -// FIXME: Thin LTO: both are safe. +// Thin LTO: both are safe. // RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c %s -flto=thin -o %t.thinlto1.bc // RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c -DBUILD2 %s -flto=thin -o %t.thinlto2.bc // RUN: llvm-lto2 run -o %t.thinlto %t.thinlto1.bc %t.thinlto2.bc -save-temps -stack-safety-print -thinlto-threads 1 -O1 \ @@ -94,9 +94,9 @@ // RUN: -r %t.thinlto1.bc,use_local,plx \ // RUN: -r %t.thinlto1.bc,w, \ // RUN: -r %t.thinlto2.bc,use,plx \ -// RUN: -r %t.thinlto2.bc,z, 2>&1 | FileCheck %s -check-prefixes=SSI,XUNSAFE,YSAFE +// RUN: -r %t.thinlto2.bc,z, 2>&1 | FileCheck %s -check-prefixes=SSI,XSAFE,YSAFE -// FIXME: Thin LTO, new PM: both are safe. +// Thin LTO, new PM: both are safe. // RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c %s -flto=thin -o %t.thinltonewpm1.bc // RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c -DBUILD2 %s -flto=thin -o %t.thinltonewpm2.bc // RUN: llvm-lto2 run -use-new-pm -o %t.thinltonewpm %t.thinltonewpm1.bc %t.thinltonewpm2.bc -save-temps -stack-safety-print -thinlto-threads 1 -O1 \ @@ -105,7 +105,7 @@ // RUN: -r %t.thinltonewpm1.bc,use_local,plx \ // RUN: -r %t.thinltonewpm1.bc,w, \ // RUN: -r %t.thinltonewpm2.bc,use,plx \ -// RUN: -r %t.thinltonewpm2.bc,z, 2>&1 | FileCheck %s -check-prefixes=SSI,XUNSAFE,YSAFE +// RUN: -r %t.thinltonewpm2.bc,z, 2>&1 | FileCheck %s -check-prefixes=SSI,XSAFE,YSAFE void use(int *p); diff --git a/llvm/include/llvm/Analysis/StackSafetyAnalysis.h b/llvm/include/llvm/Analysis/StackSafetyAnalysis.h --- a/llvm/include/llvm/Analysis/StackSafetyAnalysis.h +++ b/llvm/include/llvm/Analysis/StackSafetyAnalysis.h @@ -151,6 +151,8 @@ bool needsParamAccessSummary(const Module &M); +void generateParamAccessSummary(ModuleSummaryIndex &Index); + } // end namespace llvm #endif // LLVM_ANALYSIS_STACKSAFETYANALYSIS_H diff --git a/llvm/lib/Analysis/StackSafetyAnalysis.cpp b/llvm/lib/Analysis/StackSafetyAnalysis.cpp --- a/llvm/lib/Analysis/StackSafetyAnalysis.cpp +++ b/llvm/lib/Analysis/StackSafetyAnalysis.cpp @@ -544,6 +544,22 @@ return Functions; } +FunctionSummary *resolveCallee(GlobalValueSummary *S) { + while (S) { + if (!S->isLive() || !S->isDSOLocal()) + return nullptr; + if (FunctionSummary *FS = dyn_cast(S)) + return FS; + AliasSummary *AS = dyn_cast(S); + if (!AS) + return nullptr; + S = AS->getBaseObject(); + if (S == AS) + return nullptr; + } + return nullptr; +} + const Function *findCalleeInModule(const GlobalValue *GV) { while (GV) { if (GV->isDeclaration() || GV->isInterposable() || !GV->isDSOLocal()) @@ -560,7 +576,28 @@ return nullptr; } -template void resolveAllCalls(UseInfo &Use) { +GlobalValueSummary *getGlobalValueSummary(const ModuleSummaryIndex *Index, + uint64_t ValueGUID) { + auto VI = Index->getValueInfo(ValueGUID); + if (!VI || VI.getSummaryList().empty()) + return nullptr; + assert(VI.getSummaryList().size() == 1); + auto &Summary = VI.getSummaryList()[0]; + return Summary.get(); +} + +const ConstantRange *findParamAccess(const FunctionSummary &FS, + uint32_t ParamNo) { + assert(FS.isLive()); + assert(FS.isDSOLocal()); + for (auto &PS : FS.paramAccesses()) + if (ParamNo == PS.ParamNo) + return &PS.Use; + return nullptr; +} + +void resolveAllCalls(UseInfo &Use, + const ModuleSummaryIndex *Index) { ConstantRange FullSet(Use.Range.getBitWidth(), true); for (auto &C : Use.Calls) { const Function *F = findCalleeInModule(C.Callee); @@ -569,8 +606,24 @@ continue; } - return Use.updateRange(FullSet); + if (!Index) + return Use.updateRange(FullSet); + GlobalValueSummary *GVS = getGlobalValueSummary(Index, C.Callee->getGUID()); + + FunctionSummary *FS = resolveCallee(GVS); + if (!FS) + return Use.updateRange(FullSet); + const ConstantRange *Found = findParamAccess(*FS, C.ParamNo); + if (!Found) + return Use.updateRange(FullSet); + ConstantRange Access = Found->sextOrTrunc(Use.Range.getBitWidth()); + Use.updateRange(addOverflowNever(Access, C.Offset)); + C.Callee = nullptr; } + + Use.Calls.erase(std::remove_if(Use.Calls.begin(), Use.Calls.end(), + [](auto &T) { return !T.Callee; }), + Use.Calls.end()); } GVToSSI createGlobalStackSafetyInfo( @@ -585,7 +638,7 @@ for (auto &FnKV : Copy) for (auto &KV : FnKV.second.Params) - resolveAllCalls(KV.second); + resolveAllCalls(KV.second, Index); uint32_t PointerSize = Copy.begin() ->first->getParent() @@ -598,7 +651,7 @@ auto &SrcF = Functions[F.first]; for (auto &KV : FI.Allocas) { auto &A = KV.second; - resolveAllCalls(A); + resolveAllCalls(A, Index); for (auto &C : A.Calls) { A.updateRange( SSDFA.getArgumentAccessRange(C.Callee, C.ParamNo, C.Offset)); @@ -836,6 +889,60 @@ return false; } +void llvm::generateParamAccessSummary(ModuleSummaryIndex &Index) { + const ConstantRange FullSet(FunctionSummary::ParamAccess::RangeWidth, true); + std::map> Functions; + + // Convert the ModuleSummaryIndex to a FunctionMap + for (auto &GVS : Index) { + for (auto &GV : GVS.second.SummaryList) { + FunctionSummary *FS = dyn_cast(GV.get()); + if (!FS) + continue; + if (FS->isLive() && FS->isDSOLocal()) { + FunctionInfo FI; + for (auto &PS : FS->paramAccesses()) { + auto &US = + FI.Params + .emplace(PS.ParamNo, FunctionSummary::ParamAccess::RangeWidth) + .first->second; + US.Range = PS.Use; + for (auto &Call : PS.Calls) { + assert(!Call.Offsets.isFullSet()); + FunctionSummary *S = resolveCallee( + Index.findSummaryInModule(Call.Callee, FS->modulePath())); + if (!S) { + US.Range = FullSet; + US.Calls.clear(); + break; + } + US.Calls.emplace_back(S, Call.ParamNo, Call.Offsets); + } + } + Functions.emplace(FS, std::move(FI)); + } + // Reset data for all summaries. Alive and DSO local will be set back from + // of data flow results below. Anything else will not be accessed + // by ThinLTO backend, so we can save on bitcode size. + FS->setParamAccesses({}); + } + } + StackSafetyDataFlowAnalysis SSDFA( + FunctionSummary::ParamAccess::RangeWidth, std::move(Functions)); + for (auto &KV : SSDFA.run()) { + std::vector NewParams; + NewParams.reserve(KV.second.Params.size()); + for (auto &KV : KV.second.Params) { + NewParams.emplace_back(); + FunctionSummary::ParamAccess &New = NewParams.back(); + New.ParamNo = KV.first; + New.Use = KV.second.Range; // Only range is needed. + } + const_cast(KV.first)->setParamAccesses( + std::move(NewParams)); + } +} + static const char LocalPassArg[] = "stack-safety-local"; static const char LocalPassName[] = "Stack Safety Local Analysis"; INITIALIZE_PASS_BEGIN(StackSafetyInfoWrapperPass, LocalPassArg, LocalPassName, diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -13,6 +13,7 @@ #include "llvm/LTO/LTO.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" +#include "llvm/Analysis/StackSafetyAnalysis.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Bitcode/BitcodeReader.h" @@ -1433,6 +1434,8 @@ thinLTOResolvePrevailingInIndex(ThinLTO.CombinedIndex, isPrevailing, recordNewLinkage, GUIDPreservedSymbols); + generateParamAccessSummary(ThinLTO.CombinedIndex); + std::unique_ptr BackendProc = ThinLTO.Backend(Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries, AddStream, Cache); diff --git a/llvm/test/Analysis/StackSafetyAnalysis/ipa-alias.ll b/llvm/test/Analysis/StackSafetyAnalysis/ipa-alias.ll --- a/llvm/test/Analysis/StackSafetyAnalysis/ipa-alias.ll +++ b/llvm/test/Analysis/StackSafetyAnalysis/ipa-alias.ll @@ -11,6 +11,48 @@ ; RUN: opt -S -analyze -stack-safety %t.combined.bc | FileCheck %s --check-prefixes=CHECK,GLOBAL,NOLTO ; RUN: opt -S -passes="print-stack-safety" -disable-output %t.combined.bc 2>&1 | FileCheck %s --check-prefixes=CHECK,GLOBAL,NOLTO +; Do an end-to-test using the new LTO API +; RUN: opt -module-summary %s -o %t.summ0.bc +; RUN: opt -module-summary %S/Inputs/ipa-alias.ll -o %t.summ1.bc + +; RUN: llvm-lto2 run %t.summ0.bc %t.summ1.bc -o %t.lto -stack-safety-print -stack-safety-run -save-temps -thinlto-threads 1 -O0 \ +; RUN: -r %t.summ0.bc,PreemptableAliasWrite1, \ +; RUN: -r %t.summ0.bc,AliasToPreemptableAliasWrite1, \ +; RUN: -r %t.summ0.bc,InterposableAliasWrite1, \ +; RUN: -r %t.summ0.bc,AliasWrite1, \ +; RUN: -r %t.summ0.bc,BitcastAliasWrite1, \ +; RUN: -r %t.summ0.bc,AliasToBitcastAliasWrite1, \ +; RUN: -r %t.summ0.bc,PreemptableAliasCall,px \ +; RUN: -r %t.summ0.bc,InterposableAliasCall,px \ +; RUN: -r %t.summ0.bc,AliasCall,px \ +; RUN: -r %t.summ0.bc,BitcastAliasCall,px \ +; RUN: -r %t.summ1.bc,PreemptableAliasWrite1,px \ +; RUN: -r %t.summ1.bc,AliasToPreemptableAliasWrite1,px \ +; RUN: -r %t.summ1.bc,InterposableAliasWrite1,px \ +; RUN: -r %t.summ1.bc,AliasWrite1,px \ +; RUN: -r %t.summ1.bc,BitcastAliasWrite1,px \ +; RUN: -r %t.summ1.bc,AliasToBitcastAliasWrite1,px \ +; RUN: -r %t.summ1.bc,Write1,px 2>&1 | FileCheck %s --check-prefixes=CHECK,GLOBAL,LTO + +; RUN: llvm-lto2 run %t.summ0.bc %t.summ1.bc -o %t-newpm.lto -stack-safety-print -stack-safety-run -save-temps -use-new-pm -thinlto-threads 1 -O0 \ +; RUN: -r %t.summ0.bc,PreemptableAliasWrite1, \ +; RUN: -r %t.summ0.bc,AliasToPreemptableAliasWrite1, \ +; RUN: -r %t.summ0.bc,InterposableAliasWrite1, \ +; RUN: -r %t.summ0.bc,AliasWrite1, \ +; RUN: -r %t.summ0.bc,BitcastAliasWrite1, \ +; RUN: -r %t.summ0.bc,AliasToBitcastAliasWrite1, \ +; RUN: -r %t.summ0.bc,PreemptableAliasCall,px \ +; RUN: -r %t.summ0.bc,InterposableAliasCall,px \ +; RUN: -r %t.summ0.bc,AliasCall,px \ +; RUN: -r %t.summ0.bc,BitcastAliasCall,px \ +; RUN: -r %t.summ1.bc,PreemptableAliasWrite1,px \ +; RUN: -r %t.summ1.bc,AliasToPreemptableAliasWrite1,px \ +; RUN: -r %t.summ1.bc,InterposableAliasWrite1,px \ +; RUN: -r %t.summ1.bc,AliasWrite1,px \ +; RUN: -r %t.summ1.bc,BitcastAliasWrite1,px \ +; RUN: -r %t.summ1.bc,AliasToBitcastAliasWrite1,px \ +; RUN: -r %t.summ1.bc,Write1,px 2>&1 | FileCheck %s --check-prefixes=CHECK,GLOBAL,LTO + target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" target triple = "aarch64-unknown-linux" @@ -54,6 +96,7 @@ ; CHECK-NEXT: allocas uses: ; 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)){{$}} ; CHECK-NOT: ]: entry: %x = alloca i8 diff --git a/llvm/test/Analysis/StackSafetyAnalysis/ipa.ll b/llvm/test/Analysis/StackSafetyAnalysis/ipa.ll --- a/llvm/test/Analysis/StackSafetyAnalysis/ipa.ll +++ b/llvm/test/Analysis/StackSafetyAnalysis/ipa.ll @@ -10,6 +10,111 @@ ; RUN: opt -S -analyze -stack-safety %t.combined.bc | FileCheck %s --check-prefixes=CHECK,GLOBAL,NOLTO ; RUN: opt -S -passes="print-stack-safety" -disable-output %t.combined.bc 2>&1 | FileCheck %s --check-prefixes=CHECK,GLOBAL,NOLTO +; Do an end-to-test using the new LTO API +; TODO: Hideous llvm-lto2 invocation, add a --default-symbol-resolution to llvm-lto2? +; RUN: opt -module-summary %s -o %t.summ0.bc +; RUN: opt -module-summary %S/Inputs/ipa.ll -o %t.summ1.bc + +; RUN: llvm-lto2 run %t.summ0.bc %t.summ1.bc -o %t.lto -stack-safety-print -stack-safety-run -save-temps -thinlto-threads 1 -O0 \ +; RUN: -r %t.summ0.bc,Write1, \ +; RUN: -r %t.summ0.bc,Write4, \ +; RUN: -r %t.summ0.bc,Write4_2, \ +; RUN: -r %t.summ0.bc,Write8, \ +; RUN: -r %t.summ0.bc,WriteAndReturn8, \ +; RUN: -r %t.summ0.bc,TestUpdateArg,px \ +; RUN: -r %t.summ0.bc,ExternalCall, \ +; RUN: -r %t.summ0.bc,PreemptableWrite1, \ +; RUN: -r %t.summ0.bc,InterposableWrite1, \ +; RUN: -r %t.summ0.bc,ReturnDependent, \ +; RUN: -r %t.summ0.bc,Rec2, \ +; RUN: -r %t.summ0.bc,RecursiveNoOffset, \ +; RUN: -r %t.summ0.bc,RecursiveWithOffset, \ +; RUN: -r %t.summ0.bc,f1,px \ +; RUN: -r %t.summ0.bc,f2,px \ +; RUN: -r %t.summ0.bc,f3,px \ +; RUN: -r %t.summ0.bc,f4,px \ +; RUN: -r %t.summ0.bc,f5,px \ +; RUN: -r %t.summ0.bc,f6,px \ +; RUN: -r %t.summ0.bc,PreemptableCall,px \ +; RUN: -r %t.summ0.bc,InterposableCall,px \ +; RUN: -r %t.summ0.bc,PrivateCall,px \ +; RUN: -r %t.summ0.bc,f7,px \ +; RUN: -r %t.summ0.bc,f8left,px \ +; RUN: -r %t.summ0.bc,f8right,px \ +; RUN: -r %t.summ0.bc,f8oobleft,px \ +; RUN: -r %t.summ0.bc,f8oobright,px \ +; RUN: -r %t.summ0.bc,TwoArguments,px \ +; RUN: -r %t.summ0.bc,TwoArgumentsOOBOne,px \ +; RUN: -r %t.summ0.bc,TwoArgumentsOOBOther,px \ +; RUN: -r %t.summ0.bc,TwoArgumentsOOBBoth,px \ +; RUN: -r %t.summ0.bc,TestRecursiveNoOffset,px \ +; RUN: -r %t.summ0.bc,TestRecursiveWithOffset,px \ +; RUN: -r %t.summ1.bc,Write1,px \ +; RUN: -r %t.summ1.bc,Write4,px \ +; RUN: -r %t.summ1.bc,Write4_2,px \ +; RUN: -r %t.summ1.bc,Write8,px \ +; RUN: -r %t.summ1.bc,WriteAndReturn8,px \ +; RUN: -r %t.summ1.bc,ExternalCall,px \ +; RUN: -r %t.summ1.bc,PreemptableWrite1,px \ +; RUN: -r %t.summ1.bc,InterposableWrite1,px \ +; RUN: -r %t.summ1.bc,ReturnDependent,px \ +; RUN: -r %t.summ1.bc,Rec0,px \ +; RUN: -r %t.summ1.bc,Rec1,px \ +; RUN: -r %t.summ1.bc,Rec2,px \ +; RUN: -r %t.summ1.bc,RecursiveNoOffset,px \ +; RUN: -r %t.summ1.bc,RecursiveWithOffset,px \ +; RUN: -r %t.summ1.bc,ReturnAlloca,px 2>&1 | FileCheck %s --check-prefixes=CHECK,GLOBAL,LTO + +; RUN: llvm-lto2 run %t.summ0.bc %t.summ1.bc -o %t-newpm.lto -use-new-pm -stack-safety-print -stack-safety-run -save-temps -thinlto-threads 1 -O0 \ +; RUN: -r %t.summ0.bc,Write1, \ +; RUN: -r %t.summ0.bc,Write4, \ +; RUN: -r %t.summ0.bc,Write4_2, \ +; RUN: -r %t.summ0.bc,Write8, \ +; RUN: -r %t.summ0.bc,WriteAndReturn8, \ +; RUN: -r %t.summ0.bc,TestUpdateArg,px \ +; RUN: -r %t.summ0.bc,ExternalCall, \ +; RUN: -r %t.summ0.bc,PreemptableWrite1, \ +; RUN: -r %t.summ0.bc,InterposableWrite1, \ +; RUN: -r %t.summ0.bc,ReturnDependent, \ +; RUN: -r %t.summ0.bc,Rec2, \ +; RUN: -r %t.summ0.bc,RecursiveNoOffset, \ +; RUN: -r %t.summ0.bc,RecursiveWithOffset, \ +; RUN: -r %t.summ0.bc,f1,px \ +; RUN: -r %t.summ0.bc,f2,px \ +; RUN: -r %t.summ0.bc,f3,px \ +; RUN: -r %t.summ0.bc,f4,px \ +; RUN: -r %t.summ0.bc,f5,px \ +; RUN: -r %t.summ0.bc,f6,px \ +; RUN: -r %t.summ0.bc,PreemptableCall,px \ +; RUN: -r %t.summ0.bc,InterposableCall,px \ +; RUN: -r %t.summ0.bc,PrivateCall,px \ +; RUN: -r %t.summ0.bc,f7,px \ +; RUN: -r %t.summ0.bc,f8left,px \ +; RUN: -r %t.summ0.bc,f8right,px \ +; RUN: -r %t.summ0.bc,f8oobleft,px \ +; RUN: -r %t.summ0.bc,f8oobright,px \ +; RUN: -r %t.summ0.bc,TwoArguments,px \ +; RUN: -r %t.summ0.bc,TwoArgumentsOOBOne,px \ +; RUN: -r %t.summ0.bc,TwoArgumentsOOBOther,px \ +; RUN: -r %t.summ0.bc,TwoArgumentsOOBBoth,px \ +; RUN: -r %t.summ0.bc,TestRecursiveNoOffset,px \ +; RUN: -r %t.summ0.bc,TestRecursiveWithOffset,px \ +; RUN: -r %t.summ1.bc,Write1,px \ +; RUN: -r %t.summ1.bc,Write4,px \ +; RUN: -r %t.summ1.bc,Write4_2,px \ +; RUN: -r %t.summ1.bc,Write8,px \ +; RUN: -r %t.summ1.bc,WriteAndReturn8,px \ +; RUN: -r %t.summ1.bc,ExternalCall,px \ +; RUN: -r %t.summ1.bc,PreemptableWrite1,px \ +; RUN: -r %t.summ1.bc,InterposableWrite1,px \ +; RUN: -r %t.summ1.bc,ReturnDependent,px \ +; RUN: -r %t.summ1.bc,Rec0,px \ +; RUN: -r %t.summ1.bc,Rec1,px \ +; RUN: -r %t.summ1.bc,Rec2,px \ +; RUN: -r %t.summ1.bc,RecursiveNoOffset,px \ +; RUN: -r %t.summ1.bc,RecursiveWithOffset,px \ +; RUN: -r %t.summ1.bc,ReturnAlloca,px 2>&1 | FileCheck %s --check-prefixes=CHECK,GLOBAL,LTO + target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" target triple = "aarch64-unknown-linux" @@ -142,6 +247,7 @@ ; CHECK-NEXT: allocas uses: ; 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)){{$}} ; CHECK-NOT: ]: entry: %x = alloca i32, align 4 @@ -271,7 +377,7 @@ ret void } -define void @TwoArgumentsOOBOne() { +define void @TwoArgumentsOOBOne() #0 { ; CHECK-LABEL: @TwoArgumentsOOBOne dso_preemptable{{$}} ; CHECK-NEXT: args uses: ; CHECK-NEXT: allocas uses: @@ -318,7 +424,7 @@ ret void } -define i32 @TestRecursiveNoOffset(i32* %p, i32 %size) { +define i32 @TestRecursiveNoOffset(i32* %p, i32 %size) #0 { ; CHECK-LABEL: @TestRecursiveNoOffset dso_preemptable{{$}} ; CHECK-NEXT: args uses: ; LOCAL-NEXT: p[]: empty-set, @RecursiveNoOffset(arg0, [0,1)){{$}} @@ -416,21 +522,21 @@ ; CHECK-LABEL: @Rec0{{$}} ; CHECK-NEXT: args uses: ; LOCAL-NEXT: p[]: empty-set, @Write4(arg0, [2,3)){{$}} -; GLOBAL-NEXT: p[]: [2,6), @Write4(arg0, [2,3)){{$}} +; GLOBAL-NEXT: p[]: [2,6) ; CHECK-NEXT: allocas uses: ; CHECK-NOT: ]: ; CHECK-LABEL: @Rec1{{$}} ; CHECK-NEXT: args uses: ; LOCAL-NEXT: p[]: empty-set, @Rec0(arg0, [1,2)){{$}} -; GLOBAL-NEXT: p[]: [3,7), @Rec0(arg0, [1,2)){{$}} +; GLOBAL-NEXT: p[]: [3,7) ; CHECK-NEXT: allocas uses: ; CHECK-NOT: ]: ; CHECK-LABEL: @Rec2{{$}} ; CHECK-NEXT: args uses: ; LOCAL-NEXT: p[]: empty-set, @Rec1(arg0, [-5,-4)){{$}} -; GLOBAL-NEXT: p[]: [-2,2), @Rec1(arg0, [-5,-4)){{$}} +; GLOBAL-NEXT: p[]: [-2,2) ; CHECK-NEXT: allocas uses: ; CHECK-NOT: ]: