Skip to content

Commit

Permalink
[Attributor] Liveness for internal functions.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
sstefan1 committed Aug 20, 2019
1 parent 33c283a commit 26121ae
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 3 deletions.
3 changes: 3 additions & 0 deletions llvm/include/llvm/Transforms/IPO/Attributor.h
Expand Up @@ -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 {
Expand Down
45 changes: 43 additions & 2 deletions llvm/lib/Transforms/IPO/Attributor.cpp
Expand Up @@ -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]))
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) +=
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions llvm/test/Transforms/FunctionAttrs/align.ll
Expand Up @@ -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
}

Expand Down
97 changes: 97 additions & 0 deletions llvm/test/Transforms/FunctionAttrs/liveness.ll
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -96,13 +119,17 @@ 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

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

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 26121ae

Please sign in to comment.