/// 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<Instruction>(&getAssociatedValue()))
- if (!wouldInstructionBeTriviallyDead(I))
- indicatePessimisticFixpoint();
- if (isa<UndefValue>(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<Instruction>(U.getUser());
if (CallSite CS = CallSite(UserI)) {
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<CallBase>(I);
+ if (!CB || isa<IntrinsicInst>(CB))
+ return false;
+
+ const IRPosition &CallIRP = IRPosition::callsite_function(*CB);
+ const auto &NoUnwindAA = A.getAAFor<AANoUnwind>(*this, CallIRP);
+ if (!NoUnwindAA.isAssumedNoUnwind())
+ return false;
+
+ const auto &MemBehaviorAA = A.getAAFor<AAMemoryBehavior>(*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<UndefValue>(getAssociatedValue())) {
+ indicatePessimisticFixpoint();
+ return;
+ }
+
+ Instruction *I = dyn_cast<Instruction>(&getAssociatedValue());
+ if (!isAssumedSideEffectFree(A, I))
+ indicatePessimisticFixpoint();
+ }
+
+ /// See AbstractAttribute::updateImpl(...).
+ ChangeStatus updateImpl(Attributor &A) override {
+ Instruction *I = dyn_cast<Instruction>(&getAssociatedValue());
+ if (!isAssumedSideEffectFree(A, I))
+ return indicatePessimisticFixpoint();
+
+ if (!areAllUsesAssumedDead(A))
return indicatePessimisticFixpoint();
return ChangeStatus::UNCHANGED;
}
/// See AbstractAttribute::manifest(...).
ChangeStatus manifest(Attributor &A) override {
Value &V = getAssociatedValue();
- if (auto *I = dyn_cast<Instruction>(&V))
- if (wouldInstructionBeTriviallyDead(I)) {
+ if (auto *I = dyn_cast<Instruction>(&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<InvokeInst>(I)) {
A.deleteAfterManifest(*I);
return ChangeStatus::CHANGED;
}
-
+ }
if (V.use_empty())
return ChangeStatus::UNCHANGED;
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<UndefValue>(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<CallInst>(&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) {}
IRPosition::callsite_returned(ACS.getCallSite());
const auto &RetIsDeadAA = A.getAAFor<AAIsDead>(*this, CSRetPos);
AllKnownDead &= RetIsDeadAA.isKnownDead();
- return RetIsDeadAA.isAssumedDead();
+ return static_cast<const AAIsDeadCallSiteReturned &>(RetIsDeadAA)
+ .hasOnlyAssumedDeadUses();
};
bool AllCallSitesKnown;
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) {}
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<AAIsDead>(CSRetPos);
+
if (Function *Callee = CS.getCalledFunction()) {
// Skip declerations except if annotations on their call sites were
// explicitly requested.
IRPosition CSRetPos = IRPosition::callsite_returned(CS);
- // Call site return values might be dead.
- getOrCreateAAFor<AAIsDead>(CSRetPos);
-
// Call site return integer values might be limited by a constant range.
- if (Callee->getReturnType()->isIntegerTy()) {
+ if (Callee->getReturnType()->isIntegerTy())
getOrCreateAAFor<AAValueConstantRange>(CSRetPos);
- }
}
for (int i = 0, e = CS.getNumArgOperands(); i < e; i++) {
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
}
; 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)
; 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:
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)
; 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"
; 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
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)
; 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
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
}
; 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 }
; 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
; 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
}
; 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
}
; 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*)*))
; 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*)*))
; 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]])
; 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]])
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
}
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) {
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)
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)
-; 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
; 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
-; 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.
; 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
; CHECK-NOT: attributes #
; CHECK: attributes #{{.*}} = { nofree nosync nounwind }
; CHECK: attributes #{{.*}} = { nofree norecurse nosync nounwind }
-; CHECK: attributes #{{.*}} = { nosync }
+; CHECK: attributes #{{.*}} = { nosync nounwind }
; CHECK-NOT: attributes #
; 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:
}
-; 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 {