[llvm] 86509e8 - [Attributor] Use assumed information to determine side-effects
Johannes Doerfert via llvm-commits
llvm-commits at lists.llvm.org
Wed Feb 12 15:39:37 PST 2020
Author: Johannes Doerfert
Date: 2020-02-12T17:36:38-06:00
New Revision: 86509e8c3b846ec8f5264be8b3f53a4bef8e83e8
URL: https://github.com/llvm/llvm-project/commit/86509e8c3b846ec8f5264be8b3f53a4bef8e83e8
DIFF: https://github.com/llvm/llvm-project/commit/86509e8c3b846ec8f5264be8b3f53a4bef8e83e8.diff
LOG: [Attributor] Use assumed information to determine side-effects
We relied on wouldInstructionBeTriviallyDead before but that functions
does not take assumed information, especially for calls, into account.
The replacement, AAIsDead::isAssumeSideEffectFree, does.
This change makes AAIsDeadCallSiteReturn more complex as we can have
a dead call or only dead users.
The test have been modified to include a side effect where there was
none in order to keep the coverage.
Reviewed By: sstefan1
Differential Revision: https://reviews.llvm.org/D73311
Added:
Modified:
llvm/lib/Transforms/IPO/Attributor.cpp
llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll
llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll
llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll
llvm/test/Transforms/Attributor/align.ll
llvm/test/Transforms/Attributor/liveness.ll
llvm/test/Transforms/Attributor/misc.ll
llvm/test/Transforms/Attributor/noalias.ll
llvm/test/Transforms/Attributor/nonnull.ll
llvm/test/Transforms/Attributor/noreturn_async.ll
llvm/test/Transforms/Attributor/noreturn_sync.ll
llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll
llvm/test/Transforms/Attributor/willreturn.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index 86968d1f7761..1d0e82c45d88 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -2820,29 +2820,16 @@ struct AAIsDeadValueImpl : public AAIsDead {
/// 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)) {
@@ -2862,7 +2849,53 @@ struct AAIsDeadFloating : public AAIsDeadValueImpl {
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;
}
@@ -2870,12 +2903,16 @@ struct AAIsDeadFloating : public AAIsDeadValueImpl {
/// 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;
@@ -2956,6 +2993,69 @@ struct AAIsDeadCallSiteArgument : public AAIsDeadValueImpl {
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) {}
@@ -2970,7 +3070,8 @@ struct AAIsDeadReturned : public AAIsDeadValueImpl {
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;
@@ -3003,16 +3104,6 @@ struct AAIsDeadReturned : public AAIsDeadValueImpl {
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 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
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.
@@ -7541,13 +7638,9 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
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++) {
diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll
index a76a1c9578fd..94dad3737ba9 100644
--- a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll
+++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll
@@ -21,8 +21,10 @@ define internal void @test(i32** %X) !dbg !2 {
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
index 8ce8789bb8cc..b637d39a50de 100644
--- a/llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll
+++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll
@@ -31,8 +31,12 @@ define internal void @callee_t0f(i8* nocapture readnone %tp13, i8* nocapture rea
; 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
index ddb0e4430210..3a3f0fe210f5 100644
--- a/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll
+++ b/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll
@@ -60,7 +60,6 @@ define internal i32 @cb2(i32 %unknown) {
; 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 @@ 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)
diff --git a/llvm/test/Transforms/Attributor/align.ll b/llvm/test/Transforms/Attributor/align.ll
index 3d4a761d0812..baa7fe9cb70c 100644
--- 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 @@ define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 {
; 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 @@ define void @test8_helper() {
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
index 92142f06d4ee..cbd5484e9797 100644
--- 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 @@ define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 {
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 @@ define linkonce_odr void @non_exact3() {
; 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 @@ 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
@@ -860,6 +1054,7 @@ live_with_dead_entry:
; 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 @@ define internal void @useless_arg_sink(i32* %a) {
; 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
index 4fdb0bc280d3..cb7757915232 100644
--- a/llvm/test/Transforms/Attributor/misc.ll
+++ b/llvm/test/Transforms/Attributor/misc.ll
@@ -10,7 +10,7 @@ define internal void @internal(void (i8*)* %fp) {
; 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 @@ define internal void @internal(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 [[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 @@ define void @external(void (i8*)* %fp) {
; 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 @@ define void @external(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]])
@@ -93,11 +93,13 @@ entry:
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
index 0e95d546ea95..14b9fd0e7200 100644
--- a/llvm/test/Transforms/Attributor/noalias.ll
+++ b/llvm/test/Transforms/Attributor/noalias.ll
@@ -162,6 +162,7 @@ define i8* @test8(i32* %0) nounwind uwtable {
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
index f11f3941aaa8..fbbf0388b323 100644
--- a/llvm/test/Transforms/Attributor/nonnull.ll
+++ b/llvm/test/Transforms/Attributor/nonnull.ll
@@ -165,7 +165,7 @@ define void @test13_helper() {
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 @@ define i32* @g1() {
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
index 9fb99159acf5..de33360d8237 100644
--- 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 @@ entry:
; 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 at _0BC@NKPAGFFJ at 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 at _0BC@NKPAGFFJ at 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
index 4e6f13737a7e..cba1faacb036 100644
--- 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 @@ entry:
; 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 at _0BC@NKPAGFFJ at 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 at _0BC@NKPAGFFJ at 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
index 72d062b76161..21033beacacb 100644
--- 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 @@ entry:
; 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
index 12b79a4d009f..4cea09b94063 100644
--- a/llvm/test/Transforms/Attributor/willreturn.ll
+++ b/llvm/test/Transforms/Attributor/willreturn.ll
@@ -117,12 +117,15 @@ define i32 @fact_loop(i32 %0) local_unnamed_addr #0 {
; 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 @@ 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 {
More information about the llvm-commits
mailing list