Skip to content

Commit

Permalink
[Attributor] Liveness analysis.
Browse files Browse the repository at this point in the history
Liveness analysis abstract attribute used to indicate which BasicBlocks are dead and can therefore be ignored.
Right now we are only looking at noreturn calls.

Reviewers: jdoerfert, uenoku

Subscribers: hiraditya, llvm-commits

Differential revision: https://reviews.llvm.org/D64162

llvm-svn: 366736
  • Loading branch information
sstefan1 committed Jul 22, 2019
1 parent e35c512 commit 9285295
Show file tree
Hide file tree
Showing 5 changed files with 468 additions and 1 deletion.
40 changes: 40 additions & 0 deletions llvm/include/llvm/Transforms/IPO/Attributor.h
Expand Up @@ -805,6 +805,46 @@ struct AANoAlias : public AbstractAttribute {
static constexpr Attribute::AttrKind ID = Attribute::NoAlias;
};

/// An AbstractAttribute for noreturn.
struct AANoReturn : public AbstractAttribute {

/// See AbstractAttribute::AbstractAttribute(...).
AANoReturn(Value &V, InformationCache &InfoCache)
: AbstractAttribute(V, InfoCache) {}

/// Return true if the underlying object is known to never return.
virtual bool isKnownNoReturn() const = 0;

/// Return true if the underlying object is assumed to never return.
virtual bool isAssumedNoReturn() const = 0;

/// See AbstractAttribute::getAttrKind()
Attribute::AttrKind getAttrKind() const override { return ID; }

/// The identifier used by the Attributor for this class of attributes.
static constexpr Attribute::AttrKind ID = Attribute::NoReturn;
};

/// An abstract interface for liveness abstract attribute.
struct AAIsDead : public AbstractAttribute {

/// See AbstractAttribute::AbstractAttribute(...).
AAIsDead(Value &V, InformationCache &InfoCache)
: AbstractAttribute(V, InfoCache) {}

/// See AbstractAttribute::getAttrKind()
Attribute::AttrKind getAttrKind() const override { return ID; }

static constexpr Attribute::AttrKind ID =
Attribute::AttrKind(Attribute::EndAttrKinds + 1);

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

/// Returns true if \p BB is known dead.
virtual bool isKnownDead(BasicBlock *BB) const = 0;
};

} // end namespace llvm

#endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H
3 changes: 3 additions & 0 deletions llvm/include/llvm/Transforms/Utils/Local.h
Expand Up @@ -271,6 +271,9 @@ inline unsigned getKnownAlignment(Value *V, const DataLayout &DL,
return getOrEnforceKnownAlignment(V, 0, DL, CxtI, AC, DT);
}

/// This function converts the specified invoek into a normall call.
void changeToCall(InvokeInst *II, DomTreeUpdater *DTU = nullptr);

///===---------------------------------------------------------------------===//
/// Dbg Intrinsic utilities
///
Expand Down
174 changes: 174 additions & 0 deletions llvm/lib/Transforms/IPO/Attributor.cpp
Expand Up @@ -16,6 +16,7 @@
#include "llvm/Transforms/IPO/Attributor.h"

#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
Expand All @@ -31,6 +32,9 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Local.h"

#include <cassert>

using namespace llvm;
Expand Down Expand Up @@ -1411,6 +1415,173 @@ ChangeStatus AANoAliasReturned::updateImpl(Attributor &A) {
return ChangeStatus::UNCHANGED;
}

/// -------------------AAIsDead Function Attribute-----------------------

struct AAIsDeadFunction : AAIsDead, BooleanState {

AAIsDeadFunction(Function &F, InformationCache &InfoCache)
: AAIsDead(F, InfoCache) {}

/// See AbstractAttribute::getState()
/// {
AbstractState &getState() override { return *this; }
const AbstractState &getState() const override { return *this; }
/// }

/// See AbstractAttribute::getManifestPosition().
ManifestPosition getManifestPosition() const override { return MP_FUNCTION; }

void initialize(Attributor &A) override {
Function &F = getAnchorScope();

ToBeExploredPaths.insert(&(F.getEntryBlock().front()));
AssumedLiveBlocks.insert(&(F.getEntryBlock()));
for (size_t i = 0; i < ToBeExploredPaths.size(); ++i)
explorePath(A, ToBeExploredPaths[i]);
}

/// Explores new instructions starting from \p I. If instruction is dead, stop
/// and return true if it discovered a new instruction.
bool explorePath(Attributor &A, Instruction *I);

const std::string getAsStr() const override {
return "LiveBBs(" + std::to_string(AssumedLiveBlocks.size()) + "/" +
std::to_string(getAnchorScope().size()) + ")";
}

/// See AbstractAttribute::manifest(...).
ChangeStatus manifest(Attributor &A) override {
assert(getState().isValidState() &&
"Attempted to manifest an invalid state!");

ChangeStatus HasChanged = ChangeStatus::UNCHANGED;

for (Instruction *I : NoReturnCalls) {
BasicBlock *BB = I->getParent();

/// Invoke is replaced with a call and unreachable is placed after it.
if (auto *II = dyn_cast<InvokeInst>(I)) {
changeToCall(II);
changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false);
LLVM_DEBUG(dbgs() << "[AAIsDead] Replaced invoke with call inst\n");
continue;
}

SplitBlock(BB, I->getNextNode());
changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false);
HasChanged = ChangeStatus::CHANGED;
}

return HasChanged;
}

/// See AbstractAttribute::updateImpl(...).
ChangeStatus updateImpl(Attributor &A) override;

/// See AAIsDead::isAssumedDead().
bool isAssumedDead(BasicBlock *BB) const override {
if (!getAssumed())
return false;
return !AssumedLiveBlocks.count(BB);
}

/// See AAIsDead::isKnownDead().
bool isKnownDead(BasicBlock *BB) const override {
if (!getKnown())
return false;
return !AssumedLiveBlocks.count(BB);
}

/// Collection of to be explored paths.
SmallSetVector<Instruction *, 8> ToBeExploredPaths;

/// Collection of all assumed live BasicBlocks.
DenseSet<BasicBlock *> AssumedLiveBlocks;

/// Collection of calls with noreturn attribute, assumed or knwon.
SmallSetVector<Instruction *, 4> NoReturnCalls;
};

bool AAIsDeadFunction::explorePath(Attributor &A, Instruction *I) {
BasicBlock *BB = I->getParent();

while (I) {
ImmutableCallSite ICS(I);

if (ICS) {
auto *NoReturnAA = A.getAAFor<AANoReturn>(*this, *I);

if (NoReturnAA && NoReturnAA->isAssumedNoReturn()) {
if (!NoReturnCalls.insert(I))
// If I is already in the NoReturnCalls set, then it stayed noreturn
// and we didn't discover any new instructions.
return false;

// Discovered new noreturn call, return true to indicate that I is not
// noreturn anymore and should be deleted from NoReturnCalls.
return true;
}

if (ICS.hasFnAttr(Attribute::NoReturn)) {
if(!NoReturnCalls.insert(I))
return false;

return true;
}
}

I = I->getNextNode();
}

// get new paths (reachable blocks).
for (BasicBlock *SuccBB : successors(BB)) {
Instruction *Inst = &(SuccBB->front());
AssumedLiveBlocks.insert(SuccBB);
ToBeExploredPaths.insert(Inst);
}

return true;
}

ChangeStatus AAIsDeadFunction::updateImpl(Attributor &A) {
Function &F = getAnchorScope();

// Temporary collection to iterate over existing noreturn instructions. This
// will alow easier modification of NoReturnCalls collection
SmallVector<Instruction *, 8> NoReturnChanged;
ChangeStatus Status = ChangeStatus::UNCHANGED;

for (Instruction *I : NoReturnCalls)
NoReturnChanged.push_back(I);

for (Instruction *I : NoReturnChanged) {
size_t Size = ToBeExploredPaths.size();

// Still noreturn.
if (!explorePath(A, I))
continue;

NoReturnCalls.remove(I);

// No new paths.
if (Size == ToBeExploredPaths.size())
continue;

// At least one new path.
Status = ChangeStatus::CHANGED;

// explore new paths.
while (Size != ToBeExploredPaths.size())
explorePath(A, ToBeExploredPaths[Size++]);
}

LLVM_DEBUG(dbgs() << "[AAIsDead] AssumedLiveBlocks: "
<< AssumedLiveBlocks.size()
<< "Total number of blocks: " << F.size() << "\n");

return Status;
}

/// ----------------------------------------------------------------------------
/// Attributor
/// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -1627,6 +1798,9 @@ void Attributor::identifyDefaultAbstractAttributes(
// Every function might be "will-return".
registerAA(*new AAWillReturnFunction(F, InfoCache));

// Check for dead BasicBlocks in every function.
registerAA(*new AAIsDeadFunction(F, InfoCache));

// Walk all instructions to find more attribute opportunities and also
// interesting instructions that might be queried by abstract attributes
// during their initialization or update.
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Transforms/Utils/Local.cpp
Expand Up @@ -1964,7 +1964,7 @@ unsigned llvm::changeToUnreachable(Instruction *I, bool UseLLVMTrap,
}

/// changeToCall - Convert the specified invoke into a normal call.
static void changeToCall(InvokeInst *II, DomTreeUpdater *DTU = nullptr) {
void llvm::changeToCall(InvokeInst *II, DomTreeUpdater *DTU = nullptr) {
SmallVector<Value*, 8> Args(II->arg_begin(), II->arg_end());
SmallVector<OperandBundleDef, 1> OpBundles;
II->getOperandBundlesAsDefs(OpBundles);
Expand Down

0 comments on commit 9285295

Please sign in to comment.