forked from OSchip/llvm-project
[Attributor] Liveness for internal functions.
For an internal function, if all its call sites are dead, the body of the function is considered dead. Reviewers: jdoerfert, uenoku Subscribers: hiraditya, llvm-commits Differential Revision: https://reviews.llvm.org/D66155 llvm-svn: 369470
This commit is contained in:
parent
33c283adfd
commit
26121ae4d0
|
@ -297,6 +297,9 @@ struct IRPosition {
|
|||
if (auto *Arg = dyn_cast<Argument>(&V))
|
||||
if (!Arg->getParent()->isDeclaration())
|
||||
return &Arg->getParent()->getEntryBlock().front();
|
||||
if (auto *F = dyn_cast<Function>(&V))
|
||||
if (!F->isDeclaration())
|
||||
return &(F->getEntryBlock().front());
|
||||
return nullptr;
|
||||
}
|
||||
const Instruction *getCtxI() const {
|
||||
|
|
|
@ -1570,13 +1570,22 @@ struct AAIsDeadImpl : public AAIsDead {
|
|||
|
||||
void initialize(Attributor &A) override {
|
||||
const Function *F = getAssociatedFunction();
|
||||
|
||||
if (F->hasInternalLinkage())
|
||||
return;
|
||||
|
||||
if (!F || !F->hasExactDefinition()) {
|
||||
indicatePessimisticFixpoint();
|
||||
return;
|
||||
}
|
||||
|
||||
exploreFromEntry(A, F);
|
||||
}
|
||||
|
||||
void exploreFromEntry(Attributor &A, const Function *F) {
|
||||
ToBeExploredPaths.insert(&(F->getEntryBlock().front()));
|
||||
AssumedLiveBlocks.insert(&(F->getEntryBlock()));
|
||||
|
||||
for (size_t i = 0; i < ToBeExploredPaths.size(); ++i)
|
||||
if (const Instruction *NextNoReturnI =
|
||||
findNextNoReturn(A, ToBeExploredPaths[i]))
|
||||
|
@ -1606,7 +1615,12 @@ struct AAIsDeadImpl : public AAIsDead {
|
|||
"Attempted to manifest an invalid state!");
|
||||
|
||||
ChangeStatus HasChanged = ChangeStatus::UNCHANGED;
|
||||
const Function &F = *getAssociatedFunction();
|
||||
Function &F = *getAssociatedFunction();
|
||||
|
||||
if (AssumedLiveBlocks.empty()) {
|
||||
F.replaceAllUsesWith(UndefValue::get(F.getType()));
|
||||
return ChangeStatus::CHANGED;
|
||||
}
|
||||
|
||||
// Flag to determine if we can change an invoke to a call assuming the
|
||||
// callee is nounwind. This is not possible if the personality of the
|
||||
|
@ -1725,6 +1739,13 @@ struct AAIsDeadFunction final : public AAIsDeadImpl {
|
|||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override {
|
||||
STATS_DECL(DeadInternalFunction, Function,
|
||||
"Number of internal functions classified as dead (no live callsite)");
|
||||
BUILD_STAT_NAME(DeadInternalFunction, Function) +=
|
||||
(getAssociatedFunction()->hasInternalLinkage() &&
|
||||
AssumedLiveBlocks.empty())
|
||||
? 1
|
||||
: 0;
|
||||
STATS_DECL(DeadBlocks, Function,
|
||||
"Number of basic blocks classified as dead");
|
||||
BUILD_STAT_NAME(DeadBlocks, Function) +=
|
||||
|
@ -1796,10 +1817,25 @@ const Instruction *AAIsDeadImpl::findNextNoReturn(Attributor &A,
|
|||
}
|
||||
|
||||
ChangeStatus AAIsDeadImpl::updateImpl(Attributor &A) {
|
||||
const Function *F = getAssociatedFunction();
|
||||
ChangeStatus Status = ChangeStatus::UNCHANGED;
|
||||
|
||||
if (F->hasInternalLinkage() && AssumedLiveBlocks.empty()) {
|
||||
auto CallSiteCheck = [&](CallSite) { return false; };
|
||||
|
||||
// All callsites of F are dead.
|
||||
if (A.checkForAllCallSites(CallSiteCheck, *this, true))
|
||||
return ChangeStatus::UNCHANGED;
|
||||
|
||||
// There exists at least one live call site, so we explore the function.
|
||||
Status = ChangeStatus::CHANGED;
|
||||
|
||||
exploreFromEntry(A, F);
|
||||
}
|
||||
|
||||
// Temporary collection to iterate over existing noreturn instructions. This
|
||||
// will alow easier modification of NoReturnCalls collection
|
||||
SmallVector<const Instruction *, 8> NoReturnChanged;
|
||||
ChangeStatus Status = ChangeStatus::UNCHANGED;
|
||||
|
||||
for (const Instruction *I : NoReturnCalls)
|
||||
NoReturnChanged.push_back(I);
|
||||
|
@ -2249,6 +2285,11 @@ bool Attributor::isAssumedDead(const AbstractAttribute &AA,
|
|||
if (!LivenessAA)
|
||||
LivenessAA =
|
||||
&getAAFor<AAIsDead>(AA, IRPosition::function(*CtxI->getFunction()));
|
||||
|
||||
// Don't check liveness for AAIsDead.
|
||||
if (&AA == LivenessAA)
|
||||
return false;
|
||||
|
||||
if (!LivenessAA->isAssumedDead(CtxI))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -139,6 +139,7 @@ define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 {
|
|||
; Better than IR information
|
||||
; ATTRIBUTOR: define align 32 i32* @test7(i32* returned align 32 %p)
|
||||
define align 4 i32* @test7(i32* align 32 %p) #0 {
|
||||
tail call i8* @f1(i8* align 8 dereferenceable(1) @a1)
|
||||
ret i32* %p
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,27 @@ declare i32 @foo_noreturn() noreturn
|
|||
|
||||
declare i32 @bar() nosync readnone
|
||||
|
||||
; This internal function has no live call sites, so all its BBs are considered dead,
|
||||
; and nothing should be deduced for it.
|
||||
|
||||
; CHECK: define internal i32 @dead_internal_func(i32 %0)
|
||||
define internal i32 @dead_internal_func(i32 %0) {
|
||||
%2 = icmp slt i32 %0, 1
|
||||
br i1 %2, label %3, label %5
|
||||
|
||||
; <label>:3: ; preds = %5, %1
|
||||
%4 = phi i32 [ 1, %1 ], [ %8, %5 ]
|
||||
ret i32 %4
|
||||
|
||||
; <label>:5: ; preds = %1, %5
|
||||
%6 = phi i32 [ %9, %5 ], [ 1, %1 ]
|
||||
%7 = phi i32 [ %8, %5 ], [ 1, %1 ]
|
||||
%8 = mul nsw i32 %6, %7
|
||||
%9 = add nuw nsw i32 %6, 1
|
||||
%10 = icmp eq i32 %6, %0
|
||||
br i1 %10, label %3, label %5
|
||||
}
|
||||
|
||||
; CHECK: Function Attrs: nofree norecurse nounwind uwtable willreturn
|
||||
define i32 @volatile_load(i32*) norecurse nounwind uwtable {
|
||||
%2 = load volatile i32, i32* %0, align 4
|
||||
|
@ -35,6 +56,8 @@ entry:
|
|||
call void @no_return_call()
|
||||
; CHECK: call void @no_return_call()
|
||||
; CHECK-NEXT: unreachable
|
||||
call i32 @dead_internal_func(i32 10)
|
||||
; CHECK call i32 undef(i32 10)
|
||||
%cmp = icmp eq i32 %a, 0
|
||||
br i1 %cmp, label %cond.true, label %cond.false
|
||||
|
||||
|
@ -96,6 +119,8 @@ cond.true: ; preds = %entry
|
|||
call void @no_return_call()
|
||||
; CHECK: call void @no_return_call()
|
||||
; CHECK-NEXT: unreachable
|
||||
call i32 @dead_internal_func(i32 10)
|
||||
; CHECK call i32 undef(i32 10)
|
||||
%call = call i32 @foo()
|
||||
br label %cond.end
|
||||
|
||||
|
@ -103,6 +128,8 @@ cond.false: ; preds = %entry
|
|||
call void @no_return_call()
|
||||
; CHECK: call void @no_return_call()
|
||||
; CHECK-NEXT: unreachable
|
||||
call i32 @dead_internal_func(i32 10)
|
||||
; CHECK call i32 undef(i32 10)
|
||||
%call1 = call i32 @bar()
|
||||
br label %cond.end
|
||||
|
||||
|
@ -311,3 +338,73 @@ cond.end: ; preds = %cond.if, %con
|
|||
%8 = phi i32 [ %1, %cond.elseif ], [ 0, %cond.else ], [ 0, %cond.if ]
|
||||
ret i32 %8
|
||||
}
|
||||
|
||||
; SCC test
|
||||
;
|
||||
; char a1 __attribute__((aligned(8)));
|
||||
; char a2 __attribute__((aligned(16)));
|
||||
;
|
||||
; char* f1(char* a ){
|
||||
; return a?a:f2(&a1);
|
||||
; }
|
||||
; char* f2(char* a){
|
||||
; return a?f1(a):f3(&a2);
|
||||
; }
|
||||
;
|
||||
; char* f3(char* a){
|
||||
; return a?&a1: f1(&a2);
|
||||
; }
|
||||
|
||||
@a1 = common global i8 0, align 8
|
||||
@a2 = common global i8 0, align 16
|
||||
|
||||
define internal i8* @f1(i8* readnone %0) local_unnamed_addr #0 {
|
||||
; ATTRIBUTOR: define internal i8* @f1(i8* readnone %0)
|
||||
%2 = icmp eq i8* %0, null
|
||||
br i1 %2, label %3, label %5
|
||||
|
||||
; <label>:3: ; preds = %1
|
||||
; ATTRIBUTOR: %4 = tail call i8* undef(i8* nonnull align 8 @a1)
|
||||
%4 = tail call i8* @f2(i8* nonnull @a1)
|
||||
br label %5
|
||||
|
||||
; <label>:5: ; preds = %1, %3
|
||||
%6 = phi i8* [ %4, %3 ], [ %0, %1 ]
|
||||
ret i8* %6
|
||||
}
|
||||
|
||||
define internal i8* @f2(i8* readnone %0) local_unnamed_addr #0 {
|
||||
; ATTRIBUTOR: define internal i8* @f2(i8* readnone %0)
|
||||
%2 = icmp eq i8* %0, null
|
||||
br i1 %2, label %5, label %3
|
||||
|
||||
; <label>:3: ; preds = %1
|
||||
|
||||
; ATTRIBUTOR: %4 = tail call i8* undef(i8* nonnull align 8 %0)
|
||||
%4 = tail call i8* @f1(i8* nonnull %0)
|
||||
br label %7
|
||||
|
||||
; <label>:5: ; preds = %1
|
||||
; ATTRIBUTOR: %6 = tail call i8* undef(i8* nonnull align 16 @a2)
|
||||
%6 = tail call i8* @f3(i8* nonnull @a2)
|
||||
br label %7
|
||||
|
||||
; <label>:7: ; preds = %5, %3
|
||||
%8 = phi i8* [ %4, %3 ], [ %6, %5 ]
|
||||
ret i8* %8
|
||||
}
|
||||
|
||||
define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 {
|
||||
; ATTRIBUTOR: define internal i8* @f3(i8* readnone %0)
|
||||
%2 = icmp eq i8* %0, null
|
||||
br i1 %2, label %3, label %5
|
||||
|
||||
; <label>:3: ; preds = %1
|
||||
; ATTRIBUTOR: %4 = tail call i8* undef(i8* nonnull align 16 @a2)
|
||||
%4 = tail call i8* @f1(i8* nonnull @a2)
|
||||
br label %5
|
||||
|
||||
; <label>:5: ; preds = %1, %3
|
||||
%6 = phi i8* [ %4, %3 ], [ @a1, %1 ]
|
||||
ret i8* %6
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
; RUN: opt -functionattrs -enable-nonnull-arg-prop -attributor -attributor-disable=false -S < %s | FileCheck %s
|
||||
; RUN: opt -functionattrs -enable-nonnull-arg-prop -attributor -attributor-disable=false -attributor-max-iterations=33 -S < %s | FileCheck %s
|
||||
;
|
||||
; This is an evolved example to stress test SCC parameter attribute propagation.
|
||||
; The SCC in this test is made up of the following six function, three of which
|
||||
|
|
Loading…
Reference in New Issue