diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -2820,29 +2820,16 @@ /// See AAIsDead::isKnownDead(Instruction *I). bool isKnownDead(const Instruction *I) const override { - return I == getCtxI() && getKnown(); + return isAssumedDead(I) && getKnown(); } /// See AbstractAttribute::getAsStr(). const std::string getAsStr() const override { return isAssumedDead() ? "assumed-dead" : "assumed-live"; } -}; - -struct AAIsDeadFloating : public AAIsDeadValueImpl { - AAIsDeadFloating(const IRPosition &IRP) : AAIsDeadValueImpl(IRP) {} - /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override { - if (Instruction *I = dyn_cast(&getAssociatedValue())) - if (!wouldInstructionBeTriviallyDead(I)) - indicatePessimisticFixpoint(); - if (isa(getAssociatedValue())) - indicatePessimisticFixpoint(); - } - - /// See AbstractAttribute::updateImpl(...). - ChangeStatus updateImpl(Attributor &A) override { + /// Check if all uses are assumed dead. + bool areAllUsesAssumedDead(Attributor &A) { auto UsePred = [&](const Use &U, bool &Follow) { Instruction *UserI = cast(U.getUser()); if (CallSite CS = CallSite(UserI)) { @@ -2862,7 +2849,53 @@ return wouldInstructionBeTriviallyDead(UserI); }; - if (!A.checkForAllUses(UsePred, *this, getAssociatedValue())) + return A.checkForAllUses(UsePred, *this, getAssociatedValue()); + } + + /// Determine if \p I is assumed to be side-effect free. + bool isAssumedSideEffectFree(Attributor &A, Instruction *I) { + if (!I || wouldInstructionBeTriviallyDead(I)) + return true; + + auto *CB = dyn_cast(I); + if (!CB || isa(CB)) + return false; + + const IRPosition &CallIRP = IRPosition::callsite_function(*CB); + const auto &NoUnwindAA = A.getAAFor(*this, CallIRP); + if (!NoUnwindAA.isAssumedNoUnwind()) + return false; + + const auto &MemBehaviorAA = A.getAAFor(*this, CallIRP); + if (!MemBehaviorAA.isAssumedReadOnly()) + return false; + + return true; + } +}; + +struct AAIsDeadFloating : public AAIsDeadValueImpl { + AAIsDeadFloating(const IRPosition &IRP) : AAIsDeadValueImpl(IRP) {} + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + if (isa(getAssociatedValue())) { + indicatePessimisticFixpoint(); + return; + } + + Instruction *I = dyn_cast(&getAssociatedValue()); + if (!isAssumedSideEffectFree(A, I)) + indicatePessimisticFixpoint(); + } + + /// See AbstractAttribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override { + Instruction *I = dyn_cast(&getAssociatedValue()); + if (!isAssumedSideEffectFree(A, I)) + return indicatePessimisticFixpoint(); + + if (!areAllUsesAssumedDead(A)) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; } @@ -2870,12 +2903,16 @@ /// See AbstractAttribute::manifest(...). ChangeStatus manifest(Attributor &A) override { Value &V = getAssociatedValue(); - if (auto *I = dyn_cast(&V)) - if (wouldInstructionBeTriviallyDead(I)) { + if (auto *I = dyn_cast(&V)) { + // If we get here we basically know the users are all dead. We check if + // isAssumedSideEffectFree returns true here again because it might not be + // the case and only the users are dead but the instruction (=call) is + // still needed. + if (isAssumedSideEffectFree(A, I) && !isa(I)) { A.deleteAfterManifest(*I); return ChangeStatus::CHANGED; } - + } if (V.use_empty()) return ChangeStatus::UNCHANGED; @@ -2956,6 +2993,69 @@ void trackStatistics() const override { STATS_DECLTRACK_CSARG_ATTR(IsDead) } }; +struct AAIsDeadCallSiteReturned : public AAIsDeadFloating { + AAIsDeadCallSiteReturned(const IRPosition &IRP) + : AAIsDeadFloating(IRP), IsAssumedSideEffectFree(true) {} + + /// See AAIsDead::isAssumedDead(). + bool isAssumedDead() const override { + return AAIsDeadFloating::isAssumedDead() && IsAssumedSideEffectFree; + } + + /// Return true if all users are assumed dead. + bool hasOnlyAssumedDeadUses() const { return getAssumed(); } + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + if (isa(getAssociatedValue())) { + indicatePessimisticFixpoint(); + return; + } + + // We track this separately as a secondary state. + IsAssumedSideEffectFree = isAssumedSideEffectFree(A, getCtxI()); + } + + /// See AbstractAttribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override { + ChangeStatus Changed = ChangeStatus::UNCHANGED; + if (IsAssumedSideEffectFree && !isAssumedSideEffectFree(A, getCtxI())) { + IsAssumedSideEffectFree = false; + Changed = ChangeStatus::CHANGED; + } + + if (!areAllUsesAssumedDead(A)) + return indicatePessimisticFixpoint(); + return Changed; + } + + /// See AbstractAttribute::manifest(...). + ChangeStatus manifest(Attributor &A) override { + if (auto *CI = dyn_cast(&getAssociatedValue())) + if (CI->isMustTailCall()) + return ChangeStatus::UNCHANGED; + return AAIsDeadFloating::manifest(A); + } + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { + if (IsAssumedSideEffectFree) + STATS_DECLTRACK_CSRET_ATTR(IsDead) + else + STATS_DECLTRACK_CSRET_ATTR(UnusedResult) + } + + /// See AbstractAttribute::getAsStr(). + const std::string getAsStr() const override { + return isAssumedDead() + ? "assumed-dead" + : (getAssumed() ? "assumed-dead-users" : "assumed-live"); + } + +private: + bool IsAssumedSideEffectFree; +}; + struct AAIsDeadReturned : public AAIsDeadValueImpl { AAIsDeadReturned(const IRPosition &IRP) : AAIsDeadValueImpl(IRP) {} @@ -2970,7 +3070,8 @@ IRPosition::callsite_returned(ACS.getCallSite()); const auto &RetIsDeadAA = A.getAAFor(*this, CSRetPos); AllKnownDead &= RetIsDeadAA.isKnownDead(); - return RetIsDeadAA.isAssumedDead(); + return static_cast(RetIsDeadAA) + .hasOnlyAssumedDeadUses(); }; bool AllCallSitesKnown; @@ -3003,16 +3104,6 @@ void trackStatistics() const override { STATS_DECLTRACK_FNRET_ATTR(IsDead) } }; -struct AAIsDeadCallSiteReturned : public AAIsDeadFloating { - AAIsDeadCallSiteReturned(const IRPosition &IRP) : AAIsDeadFloating(IRP) {} - - /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override {} - - /// See AbstractAttribute::trackStatistics() - void trackStatistics() const override { STATS_DECLTRACK_CSRET_ATTR(IsDead) } -}; - struct AAIsDeadFunction : public AAIsDead { AAIsDeadFunction(const IRPosition &IRP) : AAIsDead(IRP) {} @@ -7530,6 +7621,12 @@ auto CallSitePred = [&](Instruction &I) -> bool { CallSite CS(&I); + IRPosition CSRetPos = IRPosition::callsite_returned(CS); + + // Call sites might be dead if they do not have side effects and no live + // users. The return value might be dead if there are no live users. + getOrCreateAAFor(CSRetPos); + if (Function *Callee = CS.getCalledFunction()) { // Skip declerations except if annotations on their call sites were // explicitly requested. @@ -7541,13 +7638,9 @@ IRPosition CSRetPos = IRPosition::callsite_returned(CS); - // Call site return values might be dead. - getOrCreateAAFor(CSRetPos); - // Call site return integer values might be limited by a constant range. - if (Callee->getReturnType()->isIntegerTy()) { + if (Callee->getReturnType()->isIntegerTy()) getOrCreateAAFor(CSRetPos); - } } for (int i = 0, e = CS.getNumArgOperands(); i < e; i++) { diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll @@ -21,8 +21,10 @@ define internal void @test_byval(%struct.pair* byval %P) { ; CHECK-LABEL: define {{[^@]+}}@test_byval() +; CHECK-NEXT: call void @sink(i32 0) ; CHECK-NEXT: ret void ; + call void @sink(i32 0) ret void } diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll @@ -31,8 +31,12 @@ ; CHECK-LABEL: define {{[^@]+}}@callee_t0f ; CHECK-SAME: (i8* noalias nocapture nofree nonnull readnone [[TP13:%.*]], i8* noalias nocapture nofree nonnull readnone [[TP14:%.*]], i8* noalias nocapture nofree nonnull readnone [[TP15:%.*]], i8* noalias nocapture nofree nonnull readnone [[TP16:%.*]], i8* noalias nocapture nofree nonnull readnone [[TP17:%.*]], ...) ; CHECK-NEXT: entry: +; CHECK-NEXT: call void @sink(i32 0) ; CHECK-NEXT: ret void ; entry: + call void @sink(i32 0) ret void } + +declare void @sink(i32) diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll b/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll @@ -60,7 +60,6 @@ ; CHECK-LABEL: define {{[^@]+}}@cb2 ; CHECK-SAME: (i32 returned [[UNKNOWN:%.*]]) ; CHECK-NEXT: entry: -; CHECK-NEXT: [[CALL:%.*]] = call i32 @cb0(i32 0) ; CHECK-NEXT: ret i32 [[UNKNOWN]] ; entry: @@ -91,8 +90,6 @@ define void @foo() { ; CHECK-LABEL: define {{[^@]+}}@foo() ; CHECK-NEXT: entry: -; CHECK-NEXT: [[CALL:%.*]] = call i32 @cb0(i32 0) -; CHECK-NEXT: [[CALL1:%.*]] = call i32 @cb3(i32 1) ; CHECK-NEXT: call void @broker(i32 (i32)* nonnull @cb0, i32 (i32)* nonnull @cb1, i32 (i32)* nonnull @cb0, i32 0, i32 1) ; CHECK-NEXT: call void @broker(i32 (i32)* nonnull @cb1, i32 (i32)* nonnull @cb2, i32 (i32)* nonnull @cb2, i32 0, i32 1) ; CHECK-NEXT: call void @broker(i32 (i32)* nonnull @cb3, i32 (i32)* nonnull @cb2, i32 (i32)* nonnull @cb3, i32 0, i32 1) diff --git a/llvm/test/Transforms/Attributor/align.ll b/llvm/test/Transforms/Attributor/align.ll --- a/llvm/test/Transforms/Attributor/align.ll +++ b/llvm/test/Transforms/Attributor/align.ll @@ -1,8 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes --turn off -; RUN: opt -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_MODULE -; RUN: opt -attributor-cgscc -attributor-manifest-internal -attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_CGSCC -; RUN: opt -passes=attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_MODULE -; RUN: opt -passes=attributor-cgscc -attributor-manifest-internal -attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_CGSCC +; RUN: opt -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_MODULE +; RUN: opt -attributor-cgscc -attributor-manifest-internal -attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_CGSCC +; RUN: opt -passes=attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_MODULE +; RUN: opt -passes=attributor-cgscc -attributor-manifest-internal -attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_CGSCC target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" @@ -137,18 +137,18 @@ ; TEST 7 ; Better than IR information -define align 4 i32* @test7(i32* align 32 %p) #0 { +define align 4 i8* @test7() #0 { ; ATTRIBUTOR_MODULE-LABEL: define {{[^@]+}}@test7 -; ATTRIBUTOR_MODULE-SAME: (i32* nofree readnone returned align 32 "no-capture-maybe-returned" [[P:%.*]]) -; ATTRIBUTOR_MODULE-NEXT: ret i32* [[P]] +; ATTRIBUTOR_MODULE-NEXT: [[C:%.*]] = tail call i8* @f1(i8* noalias nofree nonnull readnone align 8 dereferenceable(1) @a1) +; ATTRIBUTOR_MODULE-NEXT: ret i8* [[C]] ; ; ATTRIBUTOR_CGSCC-LABEL: define {{[^@]+}}@test7 -; ATTRIBUTOR_CGSCC-SAME: (i32* nofree readnone returned align 32 "no-capture-maybe-returned" [[P:%.*]]) -; ATTRIBUTOR_CGSCC-NEXT: [[TMP1:%.*]] = tail call i8* @f1(i8* noalias nofree nonnull readnone align 8 dereferenceable(1) @a1) -; ATTRIBUTOR_CGSCC-NEXT: ret i32* [[P]] +; ATTRIBUTOR_CGSCC-SAME: () +; ATTRIBUTOR_CGSCC-NEXT: [[C:%.*]] = tail call nonnull align 8 dereferenceable(1) i8* @f1(i8* noalias nofree nonnull readnone align 8 dereferenceable(1) @a1) +; ATTRIBUTOR_CGSCC-NEXT: ret i8* [[C]] ; - tail call i8* @f1(i8* align 8 dereferenceable(1) @a1) - ret i32* %p + %c = tail call i8* @f1(i8* align 8 dereferenceable(1) @a1) + ret i8* %c } ; TEST 7b @@ -281,7 +281,7 @@ ret void } -declare void @user_i32_ptr(i32*) readnone nounwind +declare void @user_i32_ptr(i32* nocapture readnone) nounwind define internal void @test8(i32* %a, i32* %b, i32* %c) { ; ATTRIBUTOR_MODULE: define internal void @test8(i32* noalias nocapture readnone align 4 %a, i32* noalias nocapture readnone align 4 %b, i32* noalias nocapture readnone %c) ; ATTRIBUTOR_CGSCC: define internal void @test8(i32* nocapture readnone align 4 %a, i32* nocapture readnone align 4 %b, i32* nocapture readnone %c) diff --git a/llvm/test/Transforms/Attributor/liveness.ll b/llvm/test/Transforms/Attributor/liveness.ll --- a/llvm/test/Transforms/Attributor/liveness.ll +++ b/llvm/test/Transforms/Attributor/liveness.ll @@ -8,7 +8,7 @@ ; ALL_BUT_OLD_CGSCCC: @dead_with_blockaddress_users.l = constant [2 x i8*] [i8* inttoptr (i32 1 to i8*), i8* inttoptr (i32 1 to i8*)] @dead_with_blockaddress_users.l = constant [2 x i8*] [i8* blockaddress(@dead_with_blockaddress_users, %lab0), i8* blockaddress(@dead_with_blockaddress_users, %end)] -declare void @no_return_call() nofree noreturn nounwind readnone +declare void @no_return_call() nofree noreturn nounwind nosync declare void @normal_call() readnone @@ -547,9 +547,11 @@ declare void @sink() nofree nosync nounwind willreturn define void @test_unreachable() { ; CHECK: define void @test_unreachable() +; CHECK-NEXT: call void @sink() ; CHECK-NEXT: call void @test_unreachable() ; CHECK-NEXT: unreachable ; CHECK-NEXT: } + call void @sink() call void @test_unreachable() unreachable } @@ -664,70 +666,262 @@ ; CHECK-NEXT: %nr = call i32 @foo_noreturn() ; CHECK-NEXT: unreachable -define internal void @non_dead_a0() { ret void } -define internal void @non_dead_a1() { ret void } -define internal void @non_dead_a2() { ret void } -define internal void @non_dead_a3() { ret void } -define internal void @non_dead_a4() { ret void } -define internal void @non_dead_a5() { ret void } -define internal void @non_dead_a6() { ret void } -define internal void @non_dead_a7() { ret void } -define internal void @non_dead_a8() { ret void } -define internal void @non_dead_a9() { ret void } -define internal void @non_dead_a10() { ret void } -define internal void @non_dead_a11() { ret void } -define internal void @non_dead_a12() { ret void } -define internal void @non_dead_a13() { ret void } -define internal void @non_dead_a14() { ret void } -define internal void @non_dead_a15() { ret void } -define internal void @non_dead_b0() { ret void } -define internal void @non_dead_b1() { ret void } -define internal void @non_dead_b2() { ret void } -define internal void @non_dead_b3() { ret void } -define internal void @non_dead_b4() { ret void } -define internal void @non_dead_b5() { ret void } -define internal void @non_dead_b6() { ret void } -define internal void @non_dead_b7() { ret void } -define internal void @non_dead_b8() { ret void } -define internal void @non_dead_b9() { ret void } -define internal void @non_dead_b10() { ret void } -define internal void @non_dead_b11() { ret void } -define internal void @non_dead_b12() { ret void } -define internal void @non_dead_b13() { ret void } -define internal void @non_dead_b14() { ret void } -define internal void @non_dead_b15() { ret void } -define internal void @non_dead_c0() { ret void } -define internal void @non_dead_c1() { ret void } -define internal void @non_dead_c2() { ret void } -define internal void @non_dead_c3() { ret void } -define internal void @non_dead_c4() { ret void } -define internal void @non_dead_c5() { ret void } -define internal void @non_dead_c6() { ret void } -define internal void @non_dead_c7() { ret void } -define internal void @non_dead_c8() { ret void } -define internal void @non_dead_c9() { ret void } -define internal void @non_dead_c10() { ret void } -define internal void @non_dead_c11() { ret void } -define internal void @non_dead_c12() { ret void } -define internal void @non_dead_c13() { ret void } -define internal void @non_dead_c14() { ret void } -define internal void @non_dead_c15() { ret void } -define internal void @non_dead_d0() { ret void } -define internal void @non_dead_d1() { ret void } -define internal void @non_dead_d2() { ret void } -define internal void @non_dead_d3() { ret void } -define internal void @non_dead_d4() { ret void } -define internal void @non_dead_d5() { ret void } -define internal void @non_dead_d6() { ret void } -define internal void @non_dead_d7() { ret void } -define internal void @non_dead_d8() { ret void } -define internal void @non_dead_d9() { ret void } -define internal void @non_dead_d10() { ret void } -define internal void @non_dead_d11() { ret void } -define internal void @non_dead_d12() { ret void } -define internal void @non_dead_d13() { ret void } -define internal void @non_dead_d14() { ret void } -define internal void @non_dead_d15() { ret void } +define internal void @non_dead_a0() { + call void @sink() + ret void +} +define internal void @non_dead_a1() { + call void @sink() + ret void +} +define internal void @non_dead_a2() { + call void @sink() + ret void +} +define internal void @non_dead_a3() { + call void @sink() + ret void +} +define internal void @non_dead_a4() { + call void @sink() + ret void +} +define internal void @non_dead_a5() { + call void @sink() + ret void +} +define internal void @non_dead_a6() { + call void @sink() + ret void +} +define internal void @non_dead_a7() { + call void @sink() + ret void +} +define internal void @non_dead_a8() { + call void @sink() + ret void +} +define internal void @non_dead_a9() { + call void @sink() + ret void +} +define internal void @non_dead_a10() { + call void @sink() + ret void +} +define internal void @non_dead_a11() { + call void @sink() + ret void +} +define internal void @non_dead_a12() { + call void @sink() + ret void +} +define internal void @non_dead_a13() { + call void @sink() + ret void +} +define internal void @non_dead_a14() { + call void @sink() + ret void +} +define internal void @non_dead_a15() { + call void @sink() + ret void +} +define internal void @non_dead_b0() { + call void @sink() + ret void +} +define internal void @non_dead_b1() { + call void @sink() + ret void +} +define internal void @non_dead_b2() { + call void @sink() + ret void +} +define internal void @non_dead_b3() { + call void @sink() + ret void +} +define internal void @non_dead_b4() { + call void @sink() + ret void +} +define internal void @non_dead_b5() { + call void @sink() + ret void +} +define internal void @non_dead_b6() { + call void @sink() + ret void +} +define internal void @non_dead_b7() { + call void @sink() + ret void +} +define internal void @non_dead_b8() { + call void @sink() + ret void +} +define internal void @non_dead_b9() { + call void @sink() + ret void +} +define internal void @non_dead_b10() { + call void @sink() + ret void +} +define internal void @non_dead_b11() { + call void @sink() + ret void +} +define internal void @non_dead_b12() { + call void @sink() + ret void +} +define internal void @non_dead_b13() { + call void @sink() + ret void +} +define internal void @non_dead_b14() { + call void @sink() + ret void +} +define internal void @non_dead_b15() { + call void @sink() + ret void +} +define internal void @non_dead_c0() { + call void @sink() + ret void +} +define internal void @non_dead_c1() { + call void @sink() + ret void +} +define internal void @non_dead_c2() { + call void @sink() + ret void +} +define internal void @non_dead_c3() { + call void @sink() + ret void +} +define internal void @non_dead_c4() { + call void @sink() + ret void +} +define internal void @non_dead_c5() { + call void @sink() + ret void +} +define internal void @non_dead_c6() { + call void @sink() + ret void +} +define internal void @non_dead_c7() { + call void @sink() + ret void +} +define internal void @non_dead_c8() { + call void @sink() + ret void +} +define internal void @non_dead_c9() { + call void @sink() + ret void +} +define internal void @non_dead_c10() { + call void @sink() + ret void +} +define internal void @non_dead_c11() { + call void @sink() + ret void +} +define internal void @non_dead_c12() { + call void @sink() + ret void +} +define internal void @non_dead_c13() { + call void @sink() + ret void +} +define internal void @non_dead_c14() { + call void @sink() + ret void +} +define internal void @non_dead_c15() { + call void @sink() + ret void +} +define internal void @non_dead_d0() { + call void @sink() + ret void +} +define internal void @non_dead_d1() { + call void @sink() + ret void +} +define internal void @non_dead_d2() { + call void @sink() + ret void +} +define internal void @non_dead_d3() { + call void @sink() + ret void +} +define internal void @non_dead_d4() { + call void @sink() + ret void +} +define internal void @non_dead_d5() { + call void @sink() + ret void +} +define internal void @non_dead_d6() { + call void @sink() + ret void +} +define internal void @non_dead_d7() { + call void @sink() + ret void +} +define internal void @non_dead_d8() { + call void @sink() + ret void +} +define internal void @non_dead_d9() { + call void @sink() + ret void +} +define internal void @non_dead_d10() { + call void @sink() + ret void +} +define internal void @non_dead_d11() { + call void @sink() + ret void +} +define internal void @non_dead_d12() { + call void @sink() + ret void +} +define internal void @non_dead_d13() { + call void @sink() + ret void +} +define internal void @non_dead_d14() { + call void @sink() + ret void +} +define internal void @non_dead_d15() { + call void @sink() + ret void +} define internal void @dead_e0() { call void @dead_e1() ret void } define internal void @dead_e1() { call void @dead_e2() ret void } define internal void @dead_e2() { ret void } @@ -796,10 +990,10 @@ ; CHECK: define internal void @non_dead_d13() ; CHECK: define internal void @non_dead_d14() ; Verify we actually deduce information for these functions. -; MODULE: Function Attrs: nofree nosync nounwind readnone willreturn +; MODULE: Function Attrs: nofree nosync nounwind willreturn ; MODULE-NEXT: define internal void @non_dead_d15() ; MODULE-NOT: define internal void @dead_e -; CGSCC: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; CGSCC: Function Attrs: nofree nosync nounwind willreturn ; CGSCC-NEXT: define internal void @non_dead_d15() declare void @blowup() noreturn @@ -860,6 +1054,7 @@ ; MODULE: define internal void @useless_arg_sink() ; CGSCC: define internal void @useless_arg_sink(i32*{{.*}} %a) define internal void @useless_arg_sink(i32* %a) { + call void @sink() ret void } @@ -867,7 +1062,7 @@ ; CGSCC: define internal void @useless_arg_almost_sink(i32*{{.*}} %a) define internal void @useless_arg_almost_sink(i32* %a) { ; MODULE: call void @useless_arg_sink() -; CGSCC: call void @useless_arg_sink(i32* noalias nofree readnone %a) +; CGSCC: call void @useless_arg_sink(i32* noalias nocapture nofree readnone %a) call void @useless_arg_sink(i32* %a) ret void } diff --git a/llvm/test/Transforms/Attributor/misc.ll b/llvm/test/Transforms/Attributor/misc.ll --- a/llvm/test/Transforms/Attributor/misc.ll +++ b/llvm/test/Transforms/Attributor/misc.ll @@ -10,7 +10,7 @@ ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[TMP:%.*]] = bitcast i32* [[A]] to i8* -; CHECK-NEXT: call void @foo(i32* noalias nofree nonnull readnone align 4 dereferenceable(4) undef) +; CHECK-NEXT: call void @foo(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) ; CHECK-NEXT: call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*)) ; CHECK-NEXT: call void @callback1(void (i32*)* nonnull @foo) ; CHECK-NEXT: call void @callback2(void (i8*)* bitcast (void (i32*)* @foo to void (i8*)*)) @@ -24,7 +24,7 @@ ; DECL_CS-NEXT: entry: ; DECL_CS-NEXT: [[A:%.*]] = alloca i32, align 4 ; DECL_CS-NEXT: [[TMP:%.*]] = bitcast i32* [[A]] to i8* -; DECL_CS-NEXT: call void @foo(i32* noalias nofree nonnull readnone align 4 dereferenceable(4) undef) +; DECL_CS-NEXT: call void @foo(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) ; DECL_CS-NEXT: call void [[FP]](i8* bitcast (void (i32*)* @foo to i8*)) ; DECL_CS-NEXT: call void @callback1(void (i32*)* nonnull @foo) ; DECL_CS-NEXT: call void @callback2(void (i8*)* nonnull bitcast (void (i32*)* @foo to void (i8*)*)) @@ -52,7 +52,7 @@ ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[TMP:%.*]] = bitcast i32* [[A]] to i8* -; CHECK-NEXT: call void @foo(i32* noalias nofree nonnull readnone align 4 dereferenceable(4) undef) +; CHECK-NEXT: call void @foo(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) ; CHECK-NEXT: call void @callback1(void (i32*)* nonnull @foo) ; CHECK-NEXT: call void @callback2(void (i8*)* bitcast (void (i32*)* @foo to void (i8*)*)) ; CHECK-NEXT: call void @callback2(void (i8*)* [[FP]]) @@ -67,7 +67,7 @@ ; DECL_CS-NEXT: entry: ; DECL_CS-NEXT: [[A:%.*]] = alloca i32, align 4 ; DECL_CS-NEXT: [[TMP:%.*]] = bitcast i32* [[A]] to i8* -; DECL_CS-NEXT: call void @foo(i32* noalias nofree nonnull readnone align 4 dereferenceable(4) undef) +; DECL_CS-NEXT: call void @foo(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]]) ; DECL_CS-NEXT: call void @callback1(void (i32*)* nonnull @foo) ; DECL_CS-NEXT: call void @callback2(void (i8*)* nonnull bitcast (void (i32*)* @foo to void (i8*)*)) ; DECL_CS-NEXT: call void @callback2(void (i8*)* [[FP]]) @@ -93,11 +93,13 @@ define internal void @foo(i32* %a) { ; ALL-LABEL: define {{[^@]+}}@foo -; ALL-SAME: (i32* nocapture nofree readnone [[A:%.*]]) +; ALL-SAME: (i32* nocapture nofree nonnull writeonly dereferenceable(4) [[A:%.*]]) ; ALL-NEXT: entry: +; ALL-NEXT: store i32 0, i32* %a ; ALL-NEXT: ret void ; entry: + store i32 0, i32* %a ret void } diff --git a/llvm/test/Transforms/Attributor/noalias.ll b/llvm/test/Transforms/Attributor/noalias.ll --- a/llvm/test/Transforms/Attributor/noalias.ll +++ b/llvm/test/Transforms/Attributor/noalias.ll @@ -162,6 +162,7 @@ declare void @use_i8(i8* nocapture) define internal void @test9a(i8* %a, i8* %b) { ; CHECK: define internal void @test9a() + call void @use_i8(i8* null) ret void } define internal void @test9b(i8* %a, i8* %b) { diff --git a/llvm/test/Transforms/Attributor/nonnull.ll b/llvm/test/Transforms/Attributor/nonnull.ll --- a/llvm/test/Transforms/Attributor/nonnull.ll +++ b/llvm/test/Transforms/Attributor/nonnull.ll @@ -165,7 +165,7 @@ tail call void @test13(i8* %nonnullptr, i8* %maybenullptr, i8* %nonnullptr) ret void } -declare void @use_i8_ptr(i8* nofree) readnone nounwind +declare void @use_i8_ptr(i8* nofree nocapture readnone) nounwind define internal void @test13(i8* %a, i8* %b, i8* %c) { ; ATTRIBUTOR: define internal void @test13(i8* noalias nocapture nofree nonnull readnone %a, i8* noalias nocapture nofree readnone %b, i8* noalias nocapture nofree readnone %c) call void @use_i8_ptr(i8* %a) @@ -537,7 +537,7 @@ ret i32* %c } -declare void @use_i32_ptr(i32*) readnone nounwind +declare void @use_i32_ptr(i32* readnone nocapture) nounwind ; ATTRIBUTOR: define internal void @called_by_weak(i32* noalias nocapture nonnull readnone %a) define internal void @called_by_weak(i32* %a) { call void @use_i32_ptr(i32* %a) diff --git a/llvm/test/Transforms/Attributor/noreturn_async.ll b/llvm/test/Transforms/Attributor/noreturn_async.ll --- a/llvm/test/Transforms/Attributor/noreturn_async.ll +++ b/llvm/test/Transforms/Attributor/noreturn_async.ll @@ -1,4 +1,4 @@ -; RUN: opt -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s +; RUN: opt -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s ; ; This file is the same as noreturn_sync.ll but with a personality which ; indicates that the exception handler *can* catch asynchronous exceptions. As @@ -25,8 +25,10 @@ ; CHECK: Function Attrs: nofree noreturn nosync nounwind ; CHECK-NEXT: define ; CHECK-NEXT: entry: +; CHECK-NEXT: {{.*}}@printf{{.*}} ; CHECK-NEXT: call void @"?overflow@@YAXXZ"() ; CHECK-NEXT: unreachable + %call2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @"??_C@_0BC@NKPAGFFJ@Exception?5caught?6?$AA@", i64 0, i64 0)) nofree nosync nounwind call void @"?overflow@@YAXXZ"() %call3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @"??_C@_0BC@NKPAGFFJ@Exception?5caught?6?$AA@", i64 0, i64 0)) ret void diff --git a/llvm/test/Transforms/Attributor/noreturn_sync.ll b/llvm/test/Transforms/Attributor/noreturn_sync.ll --- a/llvm/test/Transforms/Attributor/noreturn_sync.ll +++ b/llvm/test/Transforms/Attributor/noreturn_sync.ll @@ -1,4 +1,4 @@ -; RUN: opt -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s +; RUN: opt -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s ; ; This file is the same as noreturn_async.ll but with a personality which ; indicates that the exception handler *cannot* catch asynchronous exceptions. @@ -25,8 +25,10 @@ ; CHECK: Function Attrs: nofree noreturn nosync nounwind ; CHECK-NEXT: define ; CHECK-NEXT: entry: +; CHECK-NEXT: {{.*}}@printf{{.*}} ; CHECK-NEXT: call void @"?overflow@@YAXXZ"() ; CHECK-NEXT: unreachable + %call2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @"??_C@_0BC@NKPAGFFJ@Exception?5caught?6?$AA@", i64 0, i64 0)) nofree nosync nounwind call void @"?overflow@@YAXXZ"() %call3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @"??_C@_0BC@NKPAGFFJ@Exception?5caught?6?$AA@", i64 0, i64 0)) ret void diff --git a/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll b/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll --- a/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll +++ b/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll @@ -161,5 +161,5 @@ ; CHECK-NOT: attributes # ; CHECK: attributes #{{.*}} = { nofree nosync nounwind } ; CHECK: attributes #{{.*}} = { nofree norecurse nosync nounwind } -; CHECK: attributes #{{.*}} = { nosync } +; CHECK: attributes #{{.*}} = { nosync nounwind } ; CHECK-NOT: attributes # diff --git a/llvm/test/Transforms/Attributor/willreturn.ll b/llvm/test/Transforms/Attributor/willreturn.ll --- a/llvm/test/Transforms/Attributor/willreturn.ll +++ b/llvm/test/Transforms/Attributor/willreturn.ll @@ -117,12 +117,15 @@ ; mutual_recursion1(); ; } -; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable +declare void @sink() nounwind willreturn nosync nofree + +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable ; ATTRIBUTOR-NOT: willreturn ; ATTRIBUTOR-NEXT: define void @mutual_recursion1(i1 %c) define void @mutual_recursion1(i1 %c) #0 { br i1 %c, label %rec, label %end rec: + call void @sink() call void @mutual_recursion2(i1 %c) br label %end end: @@ -130,7 +133,7 @@ } -; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable ; ATTRIBUTOR-NOT: willreturn ; ATTRIBUTOR-NEXT: define void @mutual_recursion2(i1 %c) define void @mutual_recursion2(i1 %c) #0 {