From 54c73c71f7a7a6d6e68fe0d623f54e3a9f36489d Mon Sep 17 00:00:00 2001 From: Johannes Doerfert Date: Wed, 21 Jul 2021 15:11:44 -0500 Subject: [PATCH] [Attributor][FIX] Do not introduce multiple instances of SSA values If we have a recursive function we could create multiple instantiations of an SSA value, one per recursive invocation of the function. This is a problem as we use SSA value equality in various places. The basic idea follows from this test: ``` static int r(int c, int *a) { int X; return c ? r(false, &X) : a == &X; } int test(int c) { return r(c, undef); } ``` If we look through the argument `a` we will end up with `X`. Using SSA value equality we will fold `a == &X` to true and return true even though it should have been false because `a` and `&X` are from different instantiations of the function. Various tests for this have been placed in value-simplify-instances.ll and this commit fixes them all by avoiding to produce simplified values that could be non-unique at runtime. Thus, the result of a simplify value call will always be unique at runtime or the original value, both do not allow to accidentally compare two instances of a value with each other and conclude they are equal statically (pointer equivalence) while they are unequal at runtime. --- include/llvm/Transforms/IPO/Attributor.h | 6 + lib/Transforms/IPO/Attributor.cpp | 20 ++ lib/Transforms/IPO/AttributorAttributes.cpp | 38 +-- .../Transforms/Attributor/memory_locations.ll | 46 ++- .../Attributor/value-simplify-instances.ll | 265 ++++++++---------- 5 files changed, 186 insertions(+), 189 deletions(-) diff --git a/include/llvm/Transforms/IPO/Attributor.h b/include/llvm/Transforms/IPO/Attributor.h index 01b40ff7cc2..08e97292204 100644 --- a/include/llvm/Transforms/IPO/Attributor.h +++ b/include/llvm/Transforms/IPO/Attributor.h @@ -138,6 +138,12 @@ class Function; /// Abstract Attribute helper functions. namespace AA { + +/// Return true if \p V is dynamically unique, that is, there are no two +/// "instances" of \p V at runtime with different values. +bool isDynamicallyUnique(Attributor &A, const AbstractAttribute &QueryingAA, + const Value &V); + /// Return true if \p V is a valid value in \p Scope, that is a constant or an /// instruction/argument of \p Scope. bool isValidInScope(const Value &V, const Function *Scope); diff --git a/lib/Transforms/IPO/Attributor.cpp b/lib/Transforms/IPO/Attributor.cpp index 61659b295f2..c2e755ef29f 100644 --- a/lib/Transforms/IPO/Attributor.cpp +++ b/lib/Transforms/IPO/Attributor.cpp @@ -179,6 +179,26 @@ ChangeStatus &llvm::operator&=(ChangeStatus &L, ChangeStatus R) { } ///} +bool AA::isDynamicallyUnique(Attributor &A, const AbstractAttribute &QueryingAA, + const Value &V) { + if (auto *C = dyn_cast(&V)) + return !C->isThreadDependent(); + // TODO: Inspect and cache more complex instructions. + if (auto *CB = dyn_cast(&V)) + return CB->getNumOperands() == 0 && !CB->mayHaveSideEffects() && + !CB->mayReadFromMemory(); + const Function *Scope = nullptr; + if (auto *I = dyn_cast(&V)) + Scope = I->getFunction(); + if (auto *A = dyn_cast(&V)) + Scope = A->getParent(); + if (!Scope) + return false; + auto &NoRecurseAA = A.getAAFor( + QueryingAA, IRPosition::function(*Scope), DepClassTy::OPTIONAL); + return NoRecurseAA.isAssumedNoRecurse(); +} + Constant *AA::getInitialValueForObj(Value &Obj, Type &Ty) { if (isa(Obj)) return UndefValue::get(&Ty); diff --git a/lib/Transforms/IPO/AttributorAttributes.cpp b/lib/Transforms/IPO/AttributorAttributes.cpp index 6e7694a31a7..5eb586e2056 100644 --- a/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/lib/Transforms/IPO/AttributorAttributes.cpp @@ -5131,6 +5131,15 @@ struct AAValueSimplifyImpl : AAValueSimplify { static bool handleLoad(Attributor &A, const AbstractAttribute &AA, LoadInst &L, function_ref Union) { + auto UnionWrapper = [&](Value &V, Value &Obj) { + if (isa(Obj)) + return Union(V); + if (!AA::isDynamicallyUnique(A, AA, V)) + return false; + if (!AA::isValidAtPosition(V, L, A.getInfoCache())) + return false; + return Union(V); + }; Value &Ptr = *L.getPointerOperand(); SmallVector Objects; @@ -5174,10 +5183,10 @@ struct AAValueSimplifyImpl : AAValueSimplify { if (!CastedContent) return false; if (IsExact) - return Union(*CastedContent); + return UnionWrapper(*CastedContent, *Obj); if (auto *C = dyn_cast(CastedContent)) if (C->isNullValue() || C->isAllOnesValue() || isa(C)) - return Union(*CastedContent); + return UnionWrapper(*CastedContent, *Obj); return false; }; @@ -5241,32 +5250,23 @@ struct AAValueSimplifyArgument final : AAValueSimplifyImpl { // valid in the current scope. This avoids refering to simplified values // in other functions, e.g., we don't want to say a an argument in a // static function is actually an argument in a different function. - Value &ArgOp = ACSArgPos.getAssociatedValue(); bool UsedAssumedInformation = false; - Optional SimpleArgOp = - A.getAssumedSimplified(ACSArgPos, *this, UsedAssumedInformation); + Optional SimpleArgOp = + A.getAssumedConstant(ACSArgPos, *this, UsedAssumedInformation); if (!SimpleArgOp.hasValue()) return true; - Value *SimpleArgOpVal = *SimpleArgOp ? *SimpleArgOp : &ArgOp; - if (!AA::isValidInScope(*SimpleArgOpVal, getAnchorScope())) + if (!SimpleArgOp.getValue()) return false; - - // We can only propagate thread independent values through callbacks. - // This is different to direct/indirect call sites because for them we - // know the thread executing the caller and callee is the same. For - // callbacks this is not guaranteed, thus a thread dependent value could - // be different for the caller and callee, making it invalid to propagate. - if (ACS.isCallbackCall()) - if (auto *C = dyn_cast(SimpleArgOpVal)) - if (C->isThreadDependent()) - return false; - return checkAndUpdate(A, *this, ACSArgPos); + if (!AA::isDynamicallyUnique(A, *this, **SimpleArgOp)) + return false; + return unionAssumed(*SimpleArgOp); }; // Generate a answer specific to a call site context. bool Success; bool AllCallSitesKnown; - if (hasCallBaseContext()) + if (hasCallBaseContext() && + getCallBaseContext()->getCalledFunction() == Arg->getParent()) Success = PredForCallSite( AbstractCallSite(&getCallBaseContext()->getCalledOperandUse())); else diff --git a/test/Transforms/Attributor/memory_locations.ll b/test/Transforms/Attributor/memory_locations.ll index c135d02dc91..53d2894b53c 100644 --- a/test/Transforms/Attributor/memory_locations.ll +++ b/test/Transforms/Attributor/memory_locations.ll @@ -491,7 +491,7 @@ define void @writeonly_global() { ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly ; IS__CGSCC____-LABEL: define {{[^@]+}}@writeonly_global ; IS__CGSCC____-SAME: () #[[ATTR6]] { -; IS__CGSCC____-NEXT: call void @write_global() #[[ATTR11:[0-9]+]] +; IS__CGSCC____-NEXT: call void @write_global() #[[ATTR10:[0-9]+]] ; IS__CGSCC____-NEXT: ret void ; call void @write_global() @@ -507,7 +507,7 @@ define void @writeonly_global_via_arg() { ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly ; IS__CGSCC____-LABEL: define {{[^@]+}}@writeonly_global_via_arg ; IS__CGSCC____-SAME: () #[[ATTR6]] { -; IS__CGSCC____-NEXT: call void @write_global_via_arg(i32* nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) @G) #[[ATTR11]] +; IS__CGSCC____-NEXT: call void @write_global_via_arg(i32* nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) @G) #[[ATTR10]] ; IS__CGSCC____-NEXT: ret void ; call void @write_global_via_arg(i32* @G) @@ -525,7 +525,7 @@ define void @writeonly_global_via_arg_internal() { ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly ; IS__CGSCC____-LABEL: define {{[^@]+}}@writeonly_global_via_arg_internal ; IS__CGSCC____-SAME: () #[[ATTR6]] { -; IS__CGSCC____-NEXT: call void @write_global_via_arg_internal() #[[ATTR11]] +; IS__CGSCC____-NEXT: call void @write_global_via_arg_internal() #[[ATTR10]] ; IS__CGSCC____-NEXT: ret void ; call void @write_global_via_arg_internal(i32* @G) @@ -551,7 +551,7 @@ define i8 @recursive_not_readnone(i8* %ptr, i1 %c) { ; IS__CGSCC____-NEXT: [[ALLOC:%.*]] = alloca i8, align 1 ; IS__CGSCC____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] ; IS__CGSCC____: t: -; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i8 @recursive_not_readnone(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[ALLOC]], i1 noundef false) #[[ATTR12:[0-9]+]], !range [[RNG0:![0-9]+]] +; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i8 @recursive_not_readnone(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[ALLOC]], i1 noundef false) #[[ATTR11:[0-9]+]], !range [[RNG0:![0-9]+]] ; IS__CGSCC____-NEXT: ret i8 1 ; IS__CGSCC____: f: ; IS__CGSCC____-NEXT: store i8 1, i8* [[PTR]], align 1 @@ -587,7 +587,7 @@ define internal i8 @recursive_not_readnone_internal(i8* %ptr, i1 %c) { ; IS__CGSCC____-NEXT: [[ALLOC:%.*]] = alloca i8, align 1 ; IS__CGSCC____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] ; IS__CGSCC____: t: -; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i8 @recursive_not_readnone_internal(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[ALLOC]], i1 noundef false) #[[ATTR12]], !range [[RNG0]] +; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i8 @recursive_not_readnone_internal(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[ALLOC]], i1 noundef false) #[[ATTR11]], !range [[RNG0]] ; IS__CGSCC____-NEXT: ret i8 1 ; IS__CGSCC____: f: ; IS__CGSCC____-NEXT: store i8 1, i8* [[PTR]], align 1 @@ -616,7 +616,7 @@ define i8 @readnone_caller(i1 %c) { ; IS__CGSCC____-LABEL: define {{[^@]+}}@readnone_caller ; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR9:[0-9]+]] { ; IS__CGSCC____-NEXT: [[A:%.*]] = alloca i8, align 1 -; IS__CGSCC____-NEXT: [[R:%.*]] = call noundef i8 @recursive_not_readnone_internal(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[A]], i1 [[C]]) #[[ATTR13:[0-9]+]], !range [[RNG0]] +; IS__CGSCC____-NEXT: [[R:%.*]] = call noundef i8 @recursive_not_readnone_internal(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[A]], i1 [[C]]) #[[ATTR12:[0-9]+]], !range [[RNG0]] ; IS__CGSCC____-NEXT: ret i8 [[R]] ; %a = alloca i8 @@ -625,25 +625,25 @@ define i8 @readnone_caller(i1 %c) { } define internal i8 @recursive_readnone_internal2(i8* %ptr, i1 %c) { -; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone +; IS__TUNIT____: Function Attrs: argmemonly nofree nosync nounwind writeonly ; IS__TUNIT____-LABEL: define {{[^@]+}}@recursive_readnone_internal2 -; IS__TUNIT____-SAME: (i8* nocapture nofree nonnull writeonly [[PTR:%.*]], i1 [[C:%.*]]) #[[ATTR9]] { +; IS__TUNIT____-SAME: (i8* nocapture nofree nonnull writeonly [[PTR:%.*]], i1 [[C:%.*]]) #[[ATTR8]] { ; IS__TUNIT____-NEXT: [[ALLOC:%.*]] = alloca i8, align 1 ; IS__TUNIT____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] ; IS__TUNIT____: t: -; IS__TUNIT____-NEXT: [[TMP1:%.*]] = call i8 @recursive_readnone_internal2(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[ALLOC]], i1 noundef false) #[[ATTR9]], !range [[RNG0]] +; IS__TUNIT____-NEXT: [[TMP1:%.*]] = call i8 @recursive_readnone_internal2(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[ALLOC]], i1 noundef false) #[[ATTR10]], !range [[RNG0]] ; IS__TUNIT____-NEXT: ret i8 1 ; IS__TUNIT____: f: ; IS__TUNIT____-NEXT: store i8 1, i8* [[PTR]], align 1 ; IS__TUNIT____-NEXT: ret i8 0 ; -; IS__CGSCC____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__CGSCC____: Function Attrs: argmemonly nofree nosync nounwind writeonly ; IS__CGSCC____-LABEL: define {{[^@]+}}@recursive_readnone_internal2 -; IS__CGSCC____-SAME: (i8* noalias nocapture nofree nonnull readnone [[PTR:%.*]], i1 [[C:%.*]]) #[[ATTR10:[0-9]+]] { +; IS__CGSCC____-SAME: (i8* nocapture nofree nonnull writeonly [[PTR:%.*]], i1 [[C:%.*]]) #[[ATTR8]] { ; IS__CGSCC____-NEXT: [[ALLOC:%.*]] = alloca i8, align 1 ; IS__CGSCC____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] ; IS__CGSCC____: t: -; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i8 @recursive_readnone_internal2(i8* noalias nocapture nofree noundef nonnull readnone dereferenceable(1) [[ALLOC]], i1 noundef false) #[[ATTR9]], !range [[RNG0]] +; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i8 @recursive_readnone_internal2(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[ALLOC]], i1 noundef false) #[[ATTR11]], !range [[RNG0]] ; IS__CGSCC____-NEXT: ret i8 1 ; IS__CGSCC____: f: ; IS__CGSCC____-NEXT: store i8 1, i8* [[PTR]], align 1 @@ -664,13 +664,13 @@ define i8 @readnone_caller2(i1 %c) { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone ; IS__TUNIT____-LABEL: define {{[^@]+}}@readnone_caller2 ; IS__TUNIT____-SAME: (i1 [[C:%.*]]) #[[ATTR9]] { -; IS__TUNIT____-NEXT: [[R:%.*]] = call noundef i8 @recursive_readnone_internal2(i8* undef, i1 [[C]]) #[[ATTR9]], !range [[RNG0]] +; IS__TUNIT____-NEXT: [[R:%.*]] = call noundef i8 @recursive_readnone_internal2(i8* undef, i1 [[C]]) #[[ATTR10]], !range [[RNG0]] ; IS__TUNIT____-NEXT: ret i8 [[R]] ; -; IS__CGSCC____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__CGSCC____: Function Attrs: nofree nosync nounwind readnone ; IS__CGSCC____-LABEL: define {{[^@]+}}@readnone_caller2 -; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR10]] { -; IS__CGSCC____-NEXT: [[R:%.*]] = call noundef i8 @recursive_readnone_internal2(i8* undef, i1 [[C]]) #[[ATTR14:[0-9]+]], !range [[RNG0]] +; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR9]] { +; IS__CGSCC____-NEXT: [[R:%.*]] = call noundef i8 @recursive_readnone_internal2(i8* undef, i1 [[C]]) #[[ATTR12]], !range [[RNG0]] ; IS__CGSCC____-NEXT: ret i8 [[R]] ; %r = call i8 @recursive_readnone_internal2(i8* undef, i1 %c) @@ -696,7 +696,7 @@ define internal i8 @recursive_not_readnone_internal3(i8* %ptr, i1 %c) { ; IS__CGSCC____-NEXT: [[ALLOC:%.*]] = alloca i8, align 1 ; IS__CGSCC____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] ; IS__CGSCC____: t: -; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i8 @recursive_not_readnone_internal3(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[ALLOC]], i1 noundef false) #[[ATTR12]], !range [[RNG0]] +; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i8 @recursive_not_readnone_internal3(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[ALLOC]], i1 noundef false) #[[ATTR11]], !range [[RNG0]] ; IS__CGSCC____-NEXT: ret i8 1 ; IS__CGSCC____: f: ; IS__CGSCC____-NEXT: store i8 1, i8* [[PTR]], align 1 @@ -725,7 +725,7 @@ define i8 @readnone_caller3(i1 %c) { ; IS__CGSCC____-LABEL: define {{[^@]+}}@readnone_caller3 ; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR9]] { ; IS__CGSCC____-NEXT: [[ALLOC:%.*]] = alloca i8, align 1 -; IS__CGSCC____-NEXT: [[R:%.*]] = call noundef i8 @recursive_not_readnone_internal3(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[ALLOC]], i1 [[C]]) #[[ATTR13]], !range [[RNG0]] +; IS__CGSCC____-NEXT: [[R:%.*]] = call noundef i8 @recursive_not_readnone_internal3(i8* noalias nocapture nofree noundef nonnull writeonly dereferenceable(1) [[ALLOC]], i1 [[C]]) #[[ATTR12]], !range [[RNG0]] ; IS__CGSCC____-NEXT: ret i8 [[R]] ; %alloc = alloca i8 @@ -760,7 +760,7 @@ define void @argmemonky_caller() { ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly ; IS__CGSCC____-LABEL: define {{[^@]+}}@argmemonky_caller ; IS__CGSCC____-SAME: () #[[ATTR6]] { -; IS__CGSCC____-NEXT: call void @argmemonly_before_ipconstprop() #[[ATTR11]] +; IS__CGSCC____-NEXT: call void @argmemonly_before_ipconstprop() #[[ATTR10]] ; IS__CGSCC____-NEXT: ret void ; call void @argmemonly_before_ipconstprop(i32* @G) @@ -789,11 +789,9 @@ define void @argmemonky_caller() { ; IS__CGSCC____: attributes #[[ATTR7]] = { argmemonly nofree norecurse nosync nounwind willreturn writeonly } ; IS__CGSCC____: attributes #[[ATTR8]] = { argmemonly nofree nosync nounwind writeonly } ; IS__CGSCC____: attributes #[[ATTR9]] = { nofree nosync nounwind readnone } -; IS__CGSCC____: attributes #[[ATTR10]] = { nofree nosync nounwind readnone willreturn } -; IS__CGSCC____: attributes #[[ATTR11]] = { nounwind willreturn writeonly } -; IS__CGSCC____: attributes #[[ATTR12]] = { nofree nosync nounwind writeonly } -; IS__CGSCC____: attributes #[[ATTR13]] = { nounwind writeonly } -; IS__CGSCC____: attributes #[[ATTR14]] = { nounwind readnone willreturn } +; IS__CGSCC____: attributes #[[ATTR10]] = { nounwind willreturn writeonly } +; IS__CGSCC____: attributes #[[ATTR11]] = { nofree nosync nounwind writeonly } +; IS__CGSCC____: attributes #[[ATTR12]] = { nounwind writeonly } ;. ; CHECK: [[META0:![0-9]+]] = !{i8 0, i8 2} ;. diff --git a/test/Transforms/Attributor/value-simplify-instances.ll b/test/Transforms/Attributor/value-simplify-instances.ll index a9ae73bbc0f..5e95a7fa02e 100644 --- a/test/Transforms/Attributor/value-simplify-instances.ll +++ b/test/Transforms/Attributor/value-simplify-instances.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals -; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=7 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM -; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=7 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM +; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM ; RUN: opt -attributor-cgscc -enable-new-pm=0 -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM @@ -32,49 +32,27 @@ define internal i1 @recursive_inst_comparator(i1* %a, i1* %b) { } define internal i1 @recursive_inst_generator(i1 %c, i1* %p) { -; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@recursive_inst_generator -; IS__TUNIT_OPM-SAME: (i1 [[C:%.*]], i1* nofree [[P:%.*]]) { -; IS__TUNIT_OPM-NEXT: [[A:%.*]] = call i1* @geti1Ptr() -; IS__TUNIT_OPM-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] -; IS__TUNIT_OPM: t: -; IS__TUNIT_OPM-NEXT: [[R1:%.*]] = call i1 @recursive_inst_comparator(i1* noalias nofree readnone [[A]], i1* noalias nofree readnone [[P]]) #[[ATTR3:[0-9]+]] -; IS__TUNIT_OPM-NEXT: ret i1 [[R1]] -; IS__TUNIT_OPM: f: -; IS__TUNIT_OPM-NEXT: [[R2:%.*]] = call i1 @recursive_inst_generator(i1 noundef true, i1* nofree [[A]]) -; IS__TUNIT_OPM-NEXT: ret i1 [[R2]] +; IS__TUNIT____-LABEL: define {{[^@]+}}@recursive_inst_generator +; IS__TUNIT____-SAME: (i1 [[C:%.*]], i1* nofree [[P:%.*]]) { +; IS__TUNIT____-NEXT: [[A:%.*]] = call i1* @geti1Ptr() +; IS__TUNIT____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__TUNIT____: t: +; IS__TUNIT____-NEXT: [[R1:%.*]] = call i1 @recursive_inst_comparator(i1* noalias nofree readnone [[A]], i1* noalias nofree readnone [[P]]) #[[ATTR4:[0-9]+]] +; IS__TUNIT____-NEXT: ret i1 [[R1]] +; IS__TUNIT____: f: +; IS__TUNIT____-NEXT: [[R2:%.*]] = call i1 @recursive_inst_generator(i1 noundef true, i1* nofree [[A]]) +; IS__TUNIT____-NEXT: ret i1 [[R2]] ; -; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@recursive_inst_generator -; IS__TUNIT_NPM-SAME: (i1 [[C:%.*]], i1* nofree [[P:%.*]]) { -; IS__TUNIT_NPM-NEXT: [[A:%.*]] = call i1* @geti1Ptr() -; IS__TUNIT_NPM-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] -; IS__TUNIT_NPM: t: -; IS__TUNIT_NPM-NEXT: [[R1:%.*]] = call i1 @recursive_inst_comparator(i1* noalias nofree readnone [[A]], i1* noalias nofree readnone [[A]]) #[[ATTR3:[0-9]+]] -; IS__TUNIT_NPM-NEXT: ret i1 [[R1]] -; IS__TUNIT_NPM: f: -; IS__TUNIT_NPM-NEXT: [[R2:%.*]] = call i1 @recursive_inst_generator(i1 noundef true, i1* nofree [[A]]) -; IS__TUNIT_NPM-NEXT: ret i1 [[R2]] -; -; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@recursive_inst_generator -; IS__CGSCC_OPM-SAME: (i1 [[C:%.*]], i1* nofree [[P:%.*]]) { -; IS__CGSCC_OPM-NEXT: [[A:%.*]] = call i1* @geti1Ptr() -; IS__CGSCC_OPM-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] -; IS__CGSCC_OPM: t: -; IS__CGSCC_OPM-NEXT: [[R1:%.*]] = call i1 @recursive_inst_comparator(i1* noalias nofree readnone [[A]], i1* noalias nofree readnone [[P]]) -; IS__CGSCC_OPM-NEXT: ret i1 [[R1]] -; IS__CGSCC_OPM: f: -; IS__CGSCC_OPM-NEXT: [[R2:%.*]] = call i1 @recursive_inst_generator(i1 noundef true, i1* nofree [[A]]) -; IS__CGSCC_OPM-NEXT: ret i1 [[R2]] -; -; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@recursive_inst_generator -; IS__CGSCC_NPM-SAME: (i1 [[C:%.*]]) { -; IS__CGSCC_NPM-NEXT: [[A:%.*]] = call i1* @geti1Ptr() -; IS__CGSCC_NPM-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] -; IS__CGSCC_NPM: t: -; IS__CGSCC_NPM-NEXT: [[R1:%.*]] = call i1 @recursive_inst_comparator(i1* noalias nofree readnone [[A]], i1* noalias nofree readnone [[A]]) -; IS__CGSCC_NPM-NEXT: ret i1 [[R1]] -; IS__CGSCC_NPM: f: -; IS__CGSCC_NPM-NEXT: [[R2:%.*]] = call i1 @recursive_inst_generator(i1 noundef true) -; IS__CGSCC_NPM-NEXT: ret i1 [[R2]] +; IS__CGSCC____-LABEL: define {{[^@]+}}@recursive_inst_generator +; IS__CGSCC____-SAME: (i1 [[C:%.*]], i1* nofree [[P:%.*]]) { +; IS__CGSCC____-NEXT: [[A:%.*]] = call i1* @geti1Ptr() +; IS__CGSCC____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__CGSCC____: t: +; IS__CGSCC____-NEXT: [[R1:%.*]] = call i1 @recursive_inst_comparator(i1* noalias nofree readnone [[A]], i1* noalias nofree readnone [[P]]) +; IS__CGSCC____-NEXT: ret i1 [[R1]] +; IS__CGSCC____: f: +; IS__CGSCC____-NEXT: [[R2:%.*]] = call i1 @recursive_inst_generator(i1 noundef true, i1* nofree [[A]]) +; IS__CGSCC____-NEXT: ret i1 [[R2]] ; %a = call i1* @geti1Ptr() br i1 %c, label %t, label %f @@ -88,15 +66,10 @@ f: ; FIXME: This should *not* return true. define i1 @recursive_inst_generator_caller(i1 %c) { -; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@recursive_inst_generator_caller -; NOT_CGSCC_NPM-SAME: (i1 [[C:%.*]]) { -; NOT_CGSCC_NPM-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_generator(i1 [[C]], i1* undef) -; NOT_CGSCC_NPM-NEXT: ret i1 [[CALL]] -; -; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@recursive_inst_generator_caller -; IS__CGSCC_NPM-SAME: (i1 [[C:%.*]]) { -; IS__CGSCC_NPM-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_generator(i1 [[C]]) -; IS__CGSCC_NPM-NEXT: ret i1 [[CALL]] +; CHECK-LABEL: define {{[^@]+}}@recursive_inst_generator_caller +; CHECK-SAME: (i1 [[C:%.*]]) { +; CHECK-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_generator(i1 [[C]], i1* undef) +; CHECK-NEXT: ret i1 [[CALL]] ; %call = call i1 @recursive_inst_generator(i1 %c, i1* undef) ret i1 %call @@ -105,14 +78,15 @@ define i1 @recursive_inst_generator_caller(i1 %c) { ; Make sure we do *not* return true. define internal i1 @recursive_inst_compare(i1 %c, i1* %p) { ; CHECK-LABEL: define {{[^@]+}}@recursive_inst_compare -; CHECK-SAME: (i1 [[C:%.*]]) { +; CHECK-SAME: (i1 [[C:%.*]], i1* [[P:%.*]]) { ; CHECK-NEXT: [[A:%.*]] = call i1* @geti1Ptr() ; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] ; CHECK: t: -; CHECK-NEXT: ret i1 undef +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i1* [[A]], [[P]] +; CHECK-NEXT: ret i1 [[CMP]] ; CHECK: f: -; CHECK-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_compare(i1 noundef true) -; CHECK-NEXT: ret i1 undef +; CHECK-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_compare(i1 noundef true, i1* [[A]]) +; CHECK-NEXT: ret i1 [[CALL]] ; %a = call i1* @geti1Ptr() br i1 %c, label %t, label %f @@ -128,8 +102,8 @@ f: define i1 @recursive_inst_compare_caller(i1 %c) { ; CHECK-LABEL: define {{[^@]+}}@recursive_inst_compare_caller ; CHECK-SAME: (i1 [[C:%.*]]) { -; CHECK-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_compare(i1 [[C]]) -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_compare(i1 [[C]], i1* undef) +; CHECK-NEXT: ret i1 [[CALL]] ; %call = call i1 @recursive_inst_compare(i1 %c, i1* undef) ret i1 %call @@ -137,14 +111,17 @@ define i1 @recursive_inst_compare_caller(i1 %c) { ; Make sure we do *not* return true. define internal i1 @recursive_alloca_compare(i1 %c, i1* %p) { -; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn -; IS__CGSCC____-LABEL: define {{[^@]+}}@recursive_alloca_compare -; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR0]] { -; IS__CGSCC____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] -; IS__CGSCC____: t: -; IS__CGSCC____-NEXT: ret i1 undef -; IS__CGSCC____: f: -; IS__CGSCC____-NEXT: ret i1 undef +; CHECK: Function Attrs: nofree nosync nounwind readnone +; CHECK-LABEL: define {{[^@]+}}@recursive_alloca_compare +; CHECK-SAME: (i1 [[C:%.*]], i1* noalias nofree nonnull readnone [[P:%.*]]) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[A:%.*]] = alloca i1, align 1 +; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; CHECK: t: +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i1* [[A]], [[P]] +; CHECK-NEXT: ret i1 [[CMP]] +; CHECK: f: +; CHECK-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare(i1 noundef true, i1* noalias nofree noundef nonnull readnone dereferenceable(1) [[A]]) #[[ATTR1]] +; CHECK-NEXT: ret i1 [[CALL]] ; %a = alloca i1 br i1 %c, label %t, label %f @@ -158,15 +135,17 @@ f: ; FIXME: This should *not* return true. define i1 @recursive_alloca_compare_caller(i1 %c) { -; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone ; IS__TUNIT____-LABEL: define {{[^@]+}}@recursive_alloca_compare_caller -; IS__TUNIT____-SAME: (i1 [[C:%.*]]) #[[ATTR0]] { -; IS__TUNIT____-NEXT: ret i1 true +; IS__TUNIT____-SAME: (i1 [[C:%.*]]) #[[ATTR1]] { +; IS__TUNIT____-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare(i1 [[C]], i1* undef) #[[ATTR1]] +; IS__TUNIT____-NEXT: ret i1 [[CALL]] ; -; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____: Function Attrs: nofree nosync nounwind readnone ; IS__CGSCC____-LABEL: define {{[^@]+}}@recursive_alloca_compare_caller -; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR0]] { -; IS__CGSCC____-NEXT: ret i1 true +; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR1]] { +; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare(i1 [[C]], i1* undef) #[[ATTR4:[0-9]+]] +; IS__CGSCC____-NEXT: ret i1 [[CALL]] ; %call = call i1 @recursive_alloca_compare(i1 %c, i1* undef) ret i1 %call @@ -174,33 +153,19 @@ define i1 @recursive_alloca_compare_caller(i1 %c) { ; Make sure we do *not* simplify this to return 0 or 1, return 42 is ok though. define internal i8 @recursive_alloca_load_return(i1 %c, i8* %p, i8 %v) { -; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone -; IS__TUNIT____-LABEL: define {{[^@]+}}@recursive_alloca_load_return -; IS__TUNIT____-SAME: (i1 [[C:%.*]], i8* nocapture nofree nonnull readonly [[P:%.*]], i8 noundef [[V:%.*]]) #[[ATTR1:[0-9]+]] { -; IS__TUNIT____-NEXT: [[A:%.*]] = alloca i8, align 1 -; IS__TUNIT____-NEXT: store i8 [[V]], i8* [[A]], align 1 -; IS__TUNIT____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] -; IS__TUNIT____: t: -; IS__TUNIT____-NEXT: store i8 0, i8* [[A]], align 1 -; IS__TUNIT____-NEXT: [[L:%.*]] = load i8, i8* [[P]], align 1 -; IS__TUNIT____-NEXT: ret i8 [[L]] -; IS__TUNIT____: f: -; IS__TUNIT____-NEXT: [[CALL:%.*]] = call i8 @recursive_alloca_load_return(i1 noundef true, i8* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[A]], i8 noundef 1) #[[ATTR1]] -; IS__TUNIT____-NEXT: ret i8 [[CALL]] -; -; IS__CGSCC____: Function Attrs: nofree nosync nounwind readnone -; IS__CGSCC____-LABEL: define {{[^@]+}}@recursive_alloca_load_return -; IS__CGSCC____-SAME: (i1 [[C:%.*]], i8* noalias nocapture nofree nonnull readnone [[P:%.*]], i8 noundef [[V:%.*]]) #[[ATTR1:[0-9]+]] { -; IS__CGSCC____-NEXT: [[A:%.*]] = alloca i8, align 1 -; IS__CGSCC____-NEXT: store i8 [[V]], i8* [[A]], align 1 -; IS__CGSCC____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] -; IS__CGSCC____: t: -; IS__CGSCC____-NEXT: store i8 0, i8* [[A]], align 1 -; IS__CGSCC____-NEXT: [[L:%.*]] = load i8, i8* [[P]], align 1 -; IS__CGSCC____-NEXT: ret i8 [[L]] -; IS__CGSCC____: f: -; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i8 @recursive_alloca_load_return(i1 noundef true, i8* noalias nocapture nofree noundef nonnull readnone dereferenceable(1) [[A]], i8 noundef 1) #[[ATTR1]] -; IS__CGSCC____-NEXT: ret i8 [[CALL]] +; CHECK: Function Attrs: argmemonly nofree nosync nounwind +; CHECK-LABEL: define {{[^@]+}}@recursive_alloca_load_return +; CHECK-SAME: (i1 [[C:%.*]], i8* nocapture nofree nonnull readonly [[P:%.*]], i8 noundef [[V:%.*]]) #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: [[A:%.*]] = alloca i8, align 1 +; CHECK-NEXT: store i8 [[V]], i8* [[A]], align 1 +; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; CHECK: t: +; CHECK-NEXT: store i8 0, i8* [[A]], align 1 +; CHECK-NEXT: [[L:%.*]] = load i8, i8* [[P]], align 1 +; CHECK-NEXT: ret i8 [[L]] +; CHECK: f: +; CHECK-NEXT: [[CALL:%.*]] = call i8 @recursive_alloca_load_return(i1 noundef true, i8* noalias nocapture nofree noundef nonnull readonly dereferenceable(1) [[A]], i8 noundef 1) #[[ATTR3:[0-9]+]] +; CHECK-NEXT: ret i8 [[CALL]] ; %a = alloca i8 store i8 %v, i8* %a @@ -218,13 +183,13 @@ define i8 @recursive_alloca_load_return_caller(i1 %c) { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone ; IS__TUNIT____-LABEL: define {{[^@]+}}@recursive_alloca_load_return_caller ; IS__TUNIT____-SAME: (i1 [[C:%.*]]) #[[ATTR1]] { -; IS__TUNIT____-NEXT: [[CALL:%.*]] = call i8 @recursive_alloca_load_return(i1 [[C]], i8* undef, i8 noundef 42) #[[ATTR1]] +; IS__TUNIT____-NEXT: [[CALL:%.*]] = call i8 @recursive_alloca_load_return(i1 [[C]], i8* undef, i8 noundef 42) #[[ATTR3]] ; IS__TUNIT____-NEXT: ret i8 [[CALL]] ; ; IS__CGSCC____: Function Attrs: nofree nosync nounwind readnone ; IS__CGSCC____-LABEL: define {{[^@]+}}@recursive_alloca_load_return_caller ; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR1]] { -; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i8 @recursive_alloca_load_return(i1 [[C]], i8* undef, i8 noundef 42) #[[ATTR3:[0-9]+]] +; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i8 @recursive_alloca_load_return(i1 [[C]], i8* undef, i8 noundef 42) #[[ATTR5:[0-9]+]] ; IS__CGSCC____-NEXT: ret i8 [[CALL]] ; %call = call i8 @recursive_alloca_load_return(i1 %c, i8* undef, i8 42) @@ -237,17 +202,19 @@ define i8 @recursive_alloca_load_return_caller(i1 %c) { ; Make sure we do *not* return true. define internal i1 @recursive_alloca_compare_global1(i1 %c) { -; CHECK: Function Attrs: nofree nosync nounwind writeonly +; CHECK: Function Attrs: nofree nosync nounwind ; CHECK-LABEL: define {{[^@]+}}@recursive_alloca_compare_global1 -; CHECK-SAME: (i1 [[C:%.*]]) #[[ATTR2:[0-9]+]] { +; CHECK-SAME: (i1 [[C:%.*]]) #[[ATTR3]] { ; CHECK-NEXT: [[A:%.*]] = alloca i1, align 1 ; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] ; CHECK: t: -; CHECK-NEXT: ret i1 undef +; CHECK-NEXT: [[P:%.*]] = load i1*, i1** @G1, align 8 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i1* [[A]], [[P]] +; CHECK-NEXT: ret i1 [[CMP]] ; CHECK: f: ; CHECK-NEXT: store i1* [[A]], i1** @G1, align 8 -; CHECK-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare_global1(i1 noundef true) #[[ATTR2]] -; CHECK-NEXT: ret i1 undef +; CHECK-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare_global1(i1 noundef true) #[[ATTR3]] +; CHECK-NEXT: ret i1 [[CALL]] ; %a = alloca i1 br i1 %c, label %t, label %f @@ -263,34 +230,36 @@ f: ; FIXME: This should *not* return true. define i1 @recursive_alloca_compare_caller_global1(i1 %c) { -; IS__TUNIT____: Function Attrs: nofree nosync nounwind writeonly +; IS__TUNIT____: Function Attrs: nofree nosync nounwind ; IS__TUNIT____-LABEL: define {{[^@]+}}@recursive_alloca_compare_caller_global1 -; IS__TUNIT____-SAME: (i1 [[C:%.*]]) #[[ATTR2]] { -; IS__TUNIT____-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare_global1(i1 [[C]]) #[[ATTR2]] -; IS__TUNIT____-NEXT: ret i1 true +; IS__TUNIT____-SAME: (i1 [[C:%.*]]) #[[ATTR3]] { +; IS__TUNIT____-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare_global1(i1 [[C]]) #[[ATTR3]] +; IS__TUNIT____-NEXT: ret i1 [[CALL]] ; -; IS__CGSCC____: Function Attrs: nofree nosync nounwind writeonly +; IS__CGSCC____: Function Attrs: nofree nosync nounwind ; IS__CGSCC____-LABEL: define {{[^@]+}}@recursive_alloca_compare_caller_global1 -; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR2]] { -; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare_global1(i1 [[C]]) #[[ATTR4:[0-9]+]] -; IS__CGSCC____-NEXT: ret i1 true +; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR3]] { +; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare_global1(i1 [[C]]) #[[ATTR5]] +; IS__CGSCC____-NEXT: ret i1 [[CALL]] ; %call = call i1 @recursive_alloca_compare_global1(i1 %c) ret i1 %call } define internal i1 @recursive_alloca_compare_global2(i1 %c) { -; CHECK: Function Attrs: nofree nosync nounwind writeonly +; CHECK: Function Attrs: nofree nosync nounwind ; CHECK-LABEL: define {{[^@]+}}@recursive_alloca_compare_global2 -; CHECK-SAME: (i1 [[C:%.*]]) #[[ATTR2]] { +; CHECK-SAME: (i1 [[C:%.*]]) #[[ATTR3]] { ; CHECK-NEXT: [[A:%.*]] = alloca i1, align 1 +; CHECK-NEXT: [[P:%.*]] = load i1*, i1** @G2, align 8 ; CHECK-NEXT: store i1* [[A]], i1** @G2, align 8 ; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] ; CHECK: t: -; CHECK-NEXT: ret i1 undef +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i1* [[A]], [[P]] +; CHECK-NEXT: ret i1 [[CMP]] ; CHECK: f: -; CHECK-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare_global2(i1 noundef true) #[[ATTR2]] -; CHECK-NEXT: ret i1 undef +; CHECK-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare_global2(i1 noundef true) #[[ATTR3]] +; CHECK-NEXT: ret i1 [[CALL]] ; %a = alloca i1 %p = load i1*, i1** @G2 @@ -306,33 +275,35 @@ f: ; FIXME: This should *not* return true. define i1 @recursive_alloca_compare_caller_global2(i1 %c) { -; IS__TUNIT____: Function Attrs: nofree nosync nounwind writeonly +; IS__TUNIT____: Function Attrs: nofree nosync nounwind ; IS__TUNIT____-LABEL: define {{[^@]+}}@recursive_alloca_compare_caller_global2 -; IS__TUNIT____-SAME: (i1 [[C:%.*]]) #[[ATTR2]] { -; IS__TUNIT____-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare_global2(i1 [[C]]) #[[ATTR2]] -; IS__TUNIT____-NEXT: ret i1 true +; IS__TUNIT____-SAME: (i1 [[C:%.*]]) #[[ATTR3]] { +; IS__TUNIT____-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare_global2(i1 [[C]]) #[[ATTR3]] +; IS__TUNIT____-NEXT: ret i1 [[CALL]] ; -; IS__CGSCC____: Function Attrs: nofree nosync nounwind writeonly +; IS__CGSCC____: Function Attrs: nofree nosync nounwind ; IS__CGSCC____-LABEL: define {{[^@]+}}@recursive_alloca_compare_caller_global2 -; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR2]] { -; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare_global2(i1 [[C]]) #[[ATTR4]] -; IS__CGSCC____-NEXT: ret i1 true +; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR3]] { +; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i1 @recursive_alloca_compare_global2(i1 [[C]]) #[[ATTR5]] +; IS__CGSCC____-NEXT: ret i1 [[CALL]] ; %call = call i1 @recursive_alloca_compare_global2(i1 %c) ret i1 %call } define internal i1 @recursive_inst_compare_global3(i1 %c) { ; -; CHECK: Function Attrs: nofree nosync nounwind writeonly +; CHECK: Function Attrs: nofree nosync nounwind ; CHECK-LABEL: define {{[^@]+}}@recursive_inst_compare_global3 -; CHECK-SAME: (i1 [[C:%.*]]) #[[ATTR2]] { +; CHECK-SAME: (i1 [[C:%.*]]) #[[ATTR3]] { +; CHECK-NEXT: [[P:%.*]] = load i1, i1* @G3, align 1 ; CHECK-NEXT: store i1 [[C]], i1* @G3, align 1 ; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] ; CHECK: t: -; CHECK-NEXT: ret i1 undef +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i1 [[C]], [[P]] +; CHECK-NEXT: ret i1 [[CMP]] ; CHECK: f: -; CHECK-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_compare_global3(i1 noundef true) #[[ATTR2]] -; CHECK-NEXT: ret i1 undef +; CHECK-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_compare_global3(i1 noundef true) #[[ATTR3]] +; CHECK-NEXT: ret i1 [[CALL]] ; %p = load i1, i1* @G3 store i1 %c, i1* @G3 @@ -347,17 +318,17 @@ f: ; FIXME: This should *not* return true. define i1 @recursive_inst_compare_caller_global3(i1 %c) { -; IS__TUNIT____: Function Attrs: nofree nosync nounwind writeonly +; IS__TUNIT____: Function Attrs: nofree nosync nounwind ; IS__TUNIT____-LABEL: define {{[^@]+}}@recursive_inst_compare_caller_global3 -; IS__TUNIT____-SAME: (i1 [[C:%.*]]) #[[ATTR2]] { -; IS__TUNIT____-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_compare_global3(i1 [[C]]) #[[ATTR2]] -; IS__TUNIT____-NEXT: ret i1 true +; IS__TUNIT____-SAME: (i1 [[C:%.*]]) #[[ATTR3]] { +; IS__TUNIT____-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_compare_global3(i1 [[C]]) #[[ATTR3]] +; IS__TUNIT____-NEXT: ret i1 [[CALL]] ; -; IS__CGSCC____: Function Attrs: nofree nosync nounwind writeonly +; IS__CGSCC____: Function Attrs: nofree nosync nounwind ; IS__CGSCC____-LABEL: define {{[^@]+}}@recursive_inst_compare_caller_global3 -; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR2]] { -; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_compare_global3(i1 [[C]]) #[[ATTR4]] -; IS__CGSCC____-NEXT: ret i1 true +; IS__CGSCC____-SAME: (i1 [[C:%.*]]) #[[ATTR3]] { +; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i1 @recursive_inst_compare_global3(i1 [[C]]) #[[ATTR5]] +; IS__CGSCC____-NEXT: ret i1 [[CALL]] ; %call = call i1 @recursive_inst_compare_global3(i1 %c) ret i1 %call @@ -365,12 +336,14 @@ define i1 @recursive_inst_compare_caller_global3(i1 %c) { ;. ; IS__TUNIT____: attributes #[[ATTR0]] = { nofree nosync nounwind readnone willreturn } ; IS__TUNIT____: attributes #[[ATTR1]] = { nofree nosync nounwind readnone } -; IS__TUNIT____: attributes #[[ATTR2]] = { nofree nosync nounwind writeonly } -; IS__TUNIT____: attributes #[[ATTR3:[0-9]+]] = { nounwind readnone } +; IS__TUNIT____: attributes #[[ATTR2]] = { argmemonly nofree nosync nounwind } +; IS__TUNIT____: attributes #[[ATTR3]] = { nofree nosync nounwind } +; IS__TUNIT____: attributes #[[ATTR4]] = { nounwind readnone } ;. ; IS__CGSCC____: attributes #[[ATTR0]] = { nofree norecurse nosync nounwind readnone willreturn } ; IS__CGSCC____: attributes #[[ATTR1]] = { nofree nosync nounwind readnone } -; IS__CGSCC____: attributes #[[ATTR2]] = { nofree nosync nounwind writeonly } -; IS__CGSCC____: attributes #[[ATTR3]] = { nounwind readnone } -; IS__CGSCC____: attributes #[[ATTR4]] = { nounwind writeonly } +; IS__CGSCC____: attributes #[[ATTR2]] = { argmemonly nofree nosync nounwind } +; IS__CGSCC____: attributes #[[ATTR3]] = { nofree nosync nounwind } +; IS__CGSCC____: attributes #[[ATTR4]] = { nounwind readnone } +; IS__CGSCC____: attributes #[[ATTR5]] = { nounwind } ;.