Skip to content

Commit 4361da2

Browse files
author
Johannes Doerfert
committedAug 4, 2019
[Attributor][Fix] Resolve various liveness issues
Summary: This contains various fixes: - Explicitly determine and return the next noreturn instruction. - If an invoke calls a noreturn function which is not nounwind we keep the unwind destination live. This also means we require an invoke. Though we can still add the unreachable to the normal destination block. - Check if the return instructions are dead after we look for calls to avoid triggering an optimistic fixpoint in the presence of assumed liveness information. - Make the interface work with "const" pointers. - Some simplifications While additional tests are included, full coverage is achieved only with D59978. Reviewers: sstefan1, uenoku Subscribers: hiraditya, bollu, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D65701 llvm-svn: 367791
1 parent d1c3793 commit 4361da2

File tree

3 files changed

+144
-86
lines changed

3 files changed

+144
-86
lines changed
 

‎llvm/include/llvm/Transforms/IPO/Attributor.h

+7-8
Original file line numberDiff line numberDiff line change
@@ -871,26 +871,25 @@ struct AAIsDead : public AbstractAttribute {
871871
Attribute::AttrKind(Attribute::EndAttrKinds + 1);
872872

873873
/// Returns true if \p BB is assumed dead.
874-
virtual bool isAssumedDead(BasicBlock *BB) const = 0;
874+
virtual bool isAssumedDead(const BasicBlock *BB) const = 0;
875875

876876
/// Returns true if \p BB is known dead.
877-
virtual bool isKnownDead(BasicBlock *BB) const = 0;
877+
virtual bool isKnownDead(const BasicBlock *BB) const = 0;
878878

879879
/// Returns true if \p I is assumed dead.
880-
virtual bool isAssumedDead(Instruction *I) const = 0;
880+
virtual bool isAssumedDead(const Instruction *I) const = 0;
881881

882882
/// Returns true if \p I is known dead.
883-
virtual bool isKnownDead(Instruction *I) const = 0;
883+
virtual bool isKnownDead(const Instruction *I) const = 0;
884884

885885
/// This method is used to check if at least one instruction in a collection
886886
/// of instructions is live.
887887
template <typename T> bool isLiveInstSet(T begin, T end) const {
888-
for (T I = begin; I != end; ++I) {
889-
assert(isa<Instruction>(*I) && "It has to be collection of Instructions");
890-
assert((*I)->getParent()->getParent() == &getAnchorScope() &&
888+
for (const auto &I : llvm::make_range(begin, end)) {
889+
assert(I->getFunction() == &getAnchorScope() &&
891890
"Instruction must be in the same anchor scope function.");
892891

893-
if (!isAssumedDead(*I))
892+
if (!isAssumedDead(I))
894893
return true;
895894
}
896895

‎llvm/lib/Transforms/IPO/Attributor.cpp

+94-72
Original file line numberDiff line numberDiff line change
@@ -753,11 +753,6 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) {
753753
SmallPtrSet<ReturnInst *, 2> &ReturnInsts = It.second;
754754
Value *RV = It.first;
755755

756-
// Ignore dead ReturnValues.
757-
if (LivenessAA &&
758-
!LivenessAA->isLiveInstSet(ReturnInsts.begin(), ReturnInsts.end()))
759-
continue;
760-
761756
LLVM_DEBUG(dbgs() << "[AAReturnedValues] Potentially returned value " << *RV
762757
<< "\n");
763758

@@ -771,6 +766,14 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) {
771766
// sites will be removed and we will fix the information for this state.
772767
HasCallSite = true;
773768

769+
// Ignore dead ReturnValues.
770+
if (LivenessAA &&
771+
!LivenessAA->isLiveInstSet(ReturnInsts.begin(), ReturnInsts.end())) {
772+
LLVM_DEBUG(dbgs() << "[AAReturnedValues] all returns are assumed dead, "
773+
"skip it for now\n");
774+
continue;
775+
}
776+
774777
// Try to find a assumed unique return value for the called function.
775778
auto *RetCSAA = A.getAAFor<AAReturnedValuesImpl>(*this, *RV);
776779
if (!RetCSAA) {
@@ -1533,12 +1536,20 @@ struct AAIsDeadFunction : AAIsDead, BooleanState {
15331536
ToBeExploredPaths.insert(&(F.getEntryBlock().front()));
15341537
AssumedLiveBlocks.insert(&(F.getEntryBlock()));
15351538
for (size_t i = 0; i < ToBeExploredPaths.size(); ++i)
1536-
explorePath(A, ToBeExploredPaths[i]);
1539+
if (const Instruction *NextNoReturnI =
1540+
findNextNoReturn(A, ToBeExploredPaths[i]))
1541+
NoReturnCalls.insert(NextNoReturnI);
15371542
}
15381543

1539-
/// Explores new instructions starting from \p I. If instruction is dead, stop
1540-
/// and return true if it discovered a new instruction.
1541-
bool explorePath(Attributor &A, Instruction *I);
1544+
/// Find the next assumed noreturn instruction in the block of \p I starting
1545+
/// from, thus including, \p I.
1546+
///
1547+
/// The caller is responsible to monitor the ToBeExploredPaths set as new
1548+
/// instructions discovered in other basic block will be placed in there.
1549+
///
1550+
/// \returns The next assumed noreturn instructions in the block of \p I
1551+
/// starting from, thus including, \p I.
1552+
const Instruction *findNextNoReturn(Attributor &A, const Instruction *I);
15421553

15431554
const std::string getAsStr() const override {
15441555
return "LiveBBs(" + std::to_string(AssumedLiveBlocks.size()) + "/" +
@@ -1552,18 +1563,31 @@ struct AAIsDeadFunction : AAIsDead, BooleanState {
15521563

15531564
ChangeStatus HasChanged = ChangeStatus::UNCHANGED;
15541565

1555-
for (Instruction *I : NoReturnCalls) {
1566+
for (const Instruction *NRC : NoReturnCalls) {
1567+
Instruction *I = const_cast<Instruction *>(NRC);
15561568
BasicBlock *BB = I->getParent();
1569+
Instruction *SplitPos = I->getNextNode();
15571570

1558-
/// Invoke is replaced with a call and unreachable is placed after it.
15591571
if (auto *II = dyn_cast<InvokeInst>(I)) {
1560-
changeToCall(II);
1561-
changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false);
1562-
LLVM_DEBUG(dbgs() << "[AAIsDead] Replaced invoke with call inst\n");
1563-
continue;
1572+
/// Invoke is replaced with a call and unreachable is placed after it if
1573+
/// the callee is nounwind and noreturn. Otherwise, we keep the invoke
1574+
/// and only place an unreachable in the normal successor.
1575+
if (Function *Callee = II->getCalledFunction()) {
1576+
auto *AANoUnw = A.getAAFor<AANoUnwind>(*this, *Callee);
1577+
if (Callee->hasFnAttribute(Attribute::NoUnwind) ||
1578+
(AANoUnw && AANoUnw->isAssumedNoUnwind())) {
1579+
LLVM_DEBUG(dbgs() << "[AAIsDead] Replace invoke with call inst\n");
1580+
changeToCall(II);
1581+
changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false);
1582+
continue;
1583+
}
1584+
}
1585+
1586+
BB = II->getNormalDest();
1587+
SplitPos = &BB->front();
15641588
}
15651589

1566-
SplitBlock(BB, I->getNextNode());
1590+
SplitBlock(BB, SplitPos);
15671591
changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false);
15681592
HasChanged = ChangeStatus::CHANGED;
15691593
}
@@ -1575,7 +1599,7 @@ struct AAIsDeadFunction : AAIsDead, BooleanState {
15751599
ChangeStatus updateImpl(Attributor &A) override;
15761600

15771601
/// See AAIsDead::isAssumedDead(BasicBlock *).
1578-
bool isAssumedDead(BasicBlock *BB) const override {
1602+
bool isAssumedDead(const BasicBlock *BB) const override {
15791603
assert(BB->getParent() == &getAnchorScope() &&
15801604
"BB must be in the same anchor scope function.");
15811605

@@ -1585,12 +1609,12 @@ struct AAIsDeadFunction : AAIsDead, BooleanState {
15851609
}
15861610

15871611
/// See AAIsDead::isKnownDead(BasicBlock *).
1588-
bool isKnownDead(BasicBlock *BB) const override {
1612+
bool isKnownDead(const BasicBlock *BB) const override {
15891613
return getKnown() && isAssumedDead(BB);
15901614
}
15911615

15921616
/// See AAIsDead::isAssumed(Instruction *I).
1593-
bool isAssumedDead(Instruction *I) const override {
1617+
bool isAssumedDead(const Instruction *I) const override {
15941618
assert(I->getParent()->getParent() == &getAnchorScope() &&
15951619
"Instruction must be in the same anchor scope function.");
15961620

@@ -1603,33 +1627,29 @@ struct AAIsDeadFunction : AAIsDead, BooleanState {
16031627
return true;
16041628

16051629
// If it is not after a noreturn call, than it is live.
1606-
if (!isAfterNoReturn(I))
1607-
return false;
1608-
1609-
// Definitely dead.
1610-
return true;
1630+
return isAfterNoReturn(I);
16111631
}
16121632

16131633
/// See AAIsDead::isKnownDead(Instruction *I).
1614-
bool isKnownDead(Instruction *I) const override {
1634+
bool isKnownDead(const Instruction *I) const override {
16151635
return getKnown() && isAssumedDead(I);
16161636
}
16171637

16181638
/// Check if instruction is after noreturn call, in other words, assumed dead.
1619-
bool isAfterNoReturn(Instruction *I) const;
1639+
bool isAfterNoReturn(const Instruction *I) const;
16201640

16211641
/// Collection of to be explored paths.
1622-
SmallSetVector<Instruction *, 8> ToBeExploredPaths;
1642+
SmallSetVector<const Instruction *, 8> ToBeExploredPaths;
16231643

16241644
/// Collection of all assumed live BasicBlocks.
1625-
DenseSet<BasicBlock *> AssumedLiveBlocks;
1645+
DenseSet<const BasicBlock *> AssumedLiveBlocks;
16261646

16271647
/// Collection of calls with noreturn attribute, assumed or knwon.
1628-
SmallSetVector<Instruction *, 4> NoReturnCalls;
1648+
SmallSetVector<const Instruction *, 4> NoReturnCalls;
16291649
};
16301650

1631-
bool AAIsDeadFunction::isAfterNoReturn(Instruction *I) const {
1632-
Instruction *PrevI = I->getPrevNode();
1651+
bool AAIsDeadFunction::isAfterNoReturn(const Instruction *I) const {
1652+
const Instruction *PrevI = I->getPrevNode();
16331653
while (PrevI) {
16341654
if (NoReturnCalls.count(PrevI))
16351655
return true;
@@ -1638,75 +1658,77 @@ bool AAIsDeadFunction::isAfterNoReturn(Instruction *I) const {
16381658
return false;
16391659
}
16401660

1641-
bool AAIsDeadFunction::explorePath(Attributor &A, Instruction *I) {
1642-
BasicBlock *BB = I->getParent();
1661+
const Instruction *AAIsDeadFunction::findNextNoReturn(Attributor &A,
1662+
const Instruction *I) {
1663+
const BasicBlock *BB = I->getParent();
1664+
1665+
// TODO: We should have a function that determines if an "edge" is dead.
1666+
// Edges could be from an instruction to the next or from a terminator
1667+
// to the successor. For now, we need to special case the unwind block
1668+
// of InvokeInst below.
16431669

16441670
while (I) {
16451671
ImmutableCallSite ICS(I);
16461672

16471673
if (ICS) {
1648-
auto *NoReturnAA = A.getAAFor<AANoReturn>(*this, *I);
1649-
1650-
if (NoReturnAA && NoReturnAA->isAssumedNoReturn()) {
1651-
if (!NoReturnCalls.insert(I))
1652-
// If I is already in the NoReturnCalls set, then it stayed noreturn
1653-
// and we didn't discover any new instructions.
1654-
return false;
1655-
1656-
// Discovered new noreturn call, return true to indicate that I is not
1657-
// noreturn anymore and should be deleted from NoReturnCalls.
1658-
return true;
1674+
// Regarless of the no-return property of an invoke instruction we only
1675+
// learn that the regular successor is not reachable through this
1676+
// instruction but the unwind block might still be.
1677+
if (auto *Invoke = dyn_cast<InvokeInst>(I)) {
1678+
// Use nounwind to justify the unwind block is dead as well.
1679+
auto *AANoUnw = A.getAAFor<AANoUnwind>(*this, *Invoke);
1680+
if (!AANoUnw || !AANoUnw->isAssumedNoUnwind()) {
1681+
AssumedLiveBlocks.insert(Invoke->getUnwindDest());
1682+
ToBeExploredPaths.insert(&Invoke->getUnwindDest()->front());
1683+
}
16591684
}
16601685

1661-
if (ICS.hasFnAttr(Attribute::NoReturn)) {
1662-
if (!NoReturnCalls.insert(I))
1663-
return false;
1664-
1665-
return true;
1666-
}
1686+
auto *NoReturnAA = A.getAAFor<AANoReturn>(*this, *I);
1687+
if (ICS.hasFnAttr(Attribute::NoReturn) ||
1688+
(NoReturnAA && NoReturnAA->isAssumedNoReturn()))
1689+
return I;
16671690
}
16681691

16691692
I = I->getNextNode();
16701693
}
16711694

16721695
// get new paths (reachable blocks).
1673-
for (BasicBlock *SuccBB : successors(BB)) {
1674-
Instruction *Inst = &(SuccBB->front());
1696+
for (const BasicBlock *SuccBB : successors(BB)) {
16751697
AssumedLiveBlocks.insert(SuccBB);
1676-
ToBeExploredPaths.insert(Inst);
1698+
ToBeExploredPaths.insert(&SuccBB->front());
16771699
}
16781700

1679-
return true;
1701+
// No noreturn instruction found.
1702+
return nullptr;
16801703
}
16811704

16821705
ChangeStatus AAIsDeadFunction::updateImpl(Attributor &A) {
16831706
// Temporary collection to iterate over existing noreturn instructions. This
16841707
// will alow easier modification of NoReturnCalls collection
1685-
SmallVector<Instruction *, 8> NoReturnChanged;
1708+
SmallVector<const Instruction *, 8> NoReturnChanged;
16861709
ChangeStatus Status = ChangeStatus::UNCHANGED;
16871710

1688-
for (Instruction *I : NoReturnCalls)
1711+
for (const Instruction *I : NoReturnCalls)
16891712
NoReturnChanged.push_back(I);
16901713

1691-
for (Instruction *I : NoReturnChanged) {
1714+
for (const Instruction *I : NoReturnChanged) {
16921715
size_t Size = ToBeExploredPaths.size();
16931716

1694-
// Still noreturn.
1695-
if (!explorePath(A, I))
1696-
continue;
1697-
1698-
NoReturnCalls.remove(I);
1699-
1700-
// At least one new path.
1701-
Status = ChangeStatus::CHANGED;
1702-
1703-
// No new paths.
1704-
if (Size == ToBeExploredPaths.size())
1705-
continue;
1717+
const Instruction *NextNoReturnI = findNextNoReturn(A, I);
1718+
if (NextNoReturnI != I) {
1719+
Status = ChangeStatus::CHANGED;
1720+
NoReturnCalls.remove(I);
1721+
if (NextNoReturnI)
1722+
NoReturnCalls.insert(NextNoReturnI);
1723+
}
17061724

1707-
// explore new paths.
1708-
while (Size != ToBeExploredPaths.size())
1709-
explorePath(A, ToBeExploredPaths[Size++]);
1725+
// Explore new paths.
1726+
while (Size != ToBeExploredPaths.size()) {
1727+
Status = ChangeStatus::CHANGED;
1728+
if (const Instruction *NextNoReturnI =
1729+
findNextNoReturn(A, ToBeExploredPaths[Size++]))
1730+
NoReturnCalls.insert(NextNoReturnI);
1731+
}
17101732
}
17111733

17121734
LLVM_DEBUG(

‎llvm/test/Transforms/FunctionAttrs/liveness.ll

+43-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ declare void @normal_call() readnone
66

77
declare i32 @foo()
88

9+
declare i32 @foo_noreturn_nounwind() noreturn nounwind
10+
911
declare i32 @foo_noreturn() noreturn
1012

1113
declare i32 @bar() nosync readnone
@@ -134,8 +136,7 @@ cond.end: ; preds = %cond.false, %cond.t
134136
ret i32 %cond
135137
}
136138

137-
; TEST 5 noreturn invoke instruction replaced by a call and an unreachable instruction
138-
; put after it.
139+
; TEST 5 noreturn invoke instruction with a unreachable normal successor block.
139140

140141
; CHECK: define i32 @invoke_noreturn(i32 %a)
141142
define i32 @invoke_noreturn(i32 %a) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
@@ -147,8 +148,44 @@ cond.true: ; preds = %entry
147148
call void @normal_call()
148149
%call = invoke i32 @foo_noreturn() to label %continue
149150
unwind label %cleanup
150-
; CHECK: call i32 @foo_noreturn()
151-
; CHECK-NEXT unreachable
151+
; CHECK: %call = invoke i32 @foo_noreturn()
152+
; CHECK-NEXT: to label %continue unwind label %cleanup
153+
154+
cond.false: ; preds = %entry
155+
call void @normal_call()
156+
%call1 = call i32 @bar()
157+
br label %cond.end
158+
159+
cond.end: ; preds = %cond.false, %continue
160+
%cond = phi i32 [ %call, %continue ], [ %call1, %cond.false ]
161+
ret i32 %cond
162+
163+
continue:
164+
; CHECK: continue:
165+
; CHECK-NEXT: unreachable
166+
br label %cond.end
167+
168+
cleanup:
169+
%res = landingpad { i8*, i32 }
170+
catch i8* null
171+
ret i32 0
172+
}
173+
174+
; TEST 4.1 noreturn invoke instruction replaced by a call and an unreachable instruction
175+
; put after it.
176+
177+
; CHECK: define i32 @invoke_noreturn_nounwind(i32 %a)
178+
define i32 @invoke_noreturn_nounwind(i32 %a) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
179+
entry:
180+
%cmp = icmp eq i32 %a, 0
181+
br i1 %cmp, label %cond.true, label %cond.false
182+
183+
cond.true: ; preds = %entry
184+
call void @normal_call()
185+
%call = invoke i32 @foo_noreturn_nounwind() to label %continue
186+
unwind label %cleanup
187+
; CHECK: call i32 @foo_noreturn_nounwind()
188+
; CHECK-NEXT: unreachable
152189

153190
cond.false: ; preds = %entry
154191
call void @normal_call()
@@ -171,8 +208,8 @@ cleanup:
171208
; TEST 6: Undefined behvior, taken from LangRef.
172209
; FIXME: Should be able to detect undefined behavior.
173210

174-
; CHECK define @ub(i32)
175-
define void @ub(i32* ) {
211+
; CHECK: define void @ub(i32* %0)
212+
define void @ub(i32* %0) {
176213
%poison = sub nuw i32 0, 1 ; Results in a poison value.
177214
%still_poison = and i32 %poison, 0 ; 0, but also poison.
178215
%poison_yet_again = getelementptr i32, i32* %0, i32 %still_poison

0 commit comments

Comments
 (0)
Please sign in to comment.