[PM] Port DeadArgumentElimination to the new PM

The approach taken here follows r267631.

deadarghaX0r should be easy to port when the time comes to add new-PM
support to bugpoint.

llvm-svn: 272507
This commit is contained in:
Sean Silva 2016-06-12 09:16:39 +00:00
parent e75a78828b
commit ed661e0e70
5 changed files with 185 additions and 121 deletions

View File

@ -0,0 +1,133 @@
//===- DeadArgumentElimination.h - Eliminate Dead Args ----------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass deletes dead arguments from internal functions. Dead argument
// elimination removes arguments which are directly dead, as well as arguments
// only passed into function calls as dead arguments of other functions. This
// pass also deletes dead return values in a similar way.
//
// This pass is often useful as a cleanup pass to run after aggressive
// interprocedural passes, which add possibly-dead arguments or return values.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TRANSFORMS_IPO_DEADARGUMENTELIMINATION_H
#define LLVM_TRANSFORMS_IPO_DEADARGUMENTELIMINATION_H
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include <map>
#include <set>
#include <string>
namespace llvm {
/// Eliminate dead arguments (and return values) from functions.
class DeadArgumentEliminationPass
: public PassInfoMixin<DeadArgumentEliminationPass> {
public:
/// Struct that represents (part of) either a return value or a function
/// argument. Used so that arguments and return values can be used
/// interchangeably.
struct RetOrArg {
RetOrArg(const Function *F, unsigned Idx, bool IsArg)
: F(F), Idx(Idx), IsArg(IsArg) {}
const Function *F;
unsigned Idx;
bool IsArg;
/// Make RetOrArg comparable, so we can put it into a map.
bool operator<(const RetOrArg &O) const {
return std::tie(F, Idx, IsArg) < std::tie(O.F, O.Idx, O.IsArg);
}
/// Make RetOrArg comparable, so we can easily iterate the multimap.
bool operator==(const RetOrArg &O) const {
return F == O.F && Idx == O.Idx && IsArg == O.IsArg;
}
std::string getDescription() const {
return (Twine(IsArg ? "Argument #" : "Return value #") + Twine(Idx) +
" of function " + F->getName())
.str();
}
};
/// Liveness enum - During our initial pass over the program, we determine
/// that things are either alive or maybe alive. We don't mark anything
/// explicitly dead (even if we know they are), since anything not alive
/// with no registered uses (in Uses) will never be marked alive and will
/// thus become dead in the end.
enum Liveness { Live, MaybeLive };
/// Convenience wrapper
RetOrArg CreateRet(const Function *F, unsigned Idx) {
return RetOrArg(F, Idx, false);
}
/// Convenience wrapper
RetOrArg CreateArg(const Function *F, unsigned Idx) {
return RetOrArg(F, Idx, true);
}
typedef std::multimap<RetOrArg, RetOrArg> UseMap;
/// This maps a return value or argument to any MaybeLive return values or
/// arguments it uses. This allows the MaybeLive values to be marked live
/// when any of its users is marked live.
/// For example (indices are left out for clarity):
/// - Uses[ret F] = ret G
/// This means that F calls G, and F returns the value returned by G.
/// - Uses[arg F] = ret G
/// This means that some function calls G and passes its result as an
/// argument to F.
/// - Uses[ret F] = arg F
/// This means that F returns one of its own arguments.
/// - Uses[arg F] = arg G
/// This means that G calls F and passes one of its own (G's) arguments
/// directly to F.
UseMap Uses;
typedef std::set<RetOrArg> LiveSet;
typedef std::set<const Function *> LiveFuncSet;
/// This set contains all values that have been determined to be live.
LiveSet LiveValues;
/// This set contains all values that are cannot be changed in any way.
LiveFuncSet LiveFunctions;
typedef SmallVector<RetOrArg, 5> UseVector;
/// This allows this pass to do double-duty as the dead arg hacking pass
/// (used only by bugpoint).
bool ShouldHackArguments = false;
public:
DeadArgumentEliminationPass(bool ShouldHackArguments_ = false)
: ShouldHackArguments(ShouldHackArguments_) {}
PreservedAnalyses run(Module &M);
private:
Liveness MarkIfNotLive(RetOrArg Use, UseVector &MaybeLiveUses);
Liveness SurveyUse(const Use *U, UseVector &MaybeLiveUses,
unsigned RetValNum = -1U);
Liveness SurveyUses(const Value *V, UseVector &MaybeLiveUses);
void SurveyFunction(const Function &F);
void MarkValue(const RetOrArg &RA, Liveness L,
const UseVector &MaybeLiveUses);
void MarkLive(const RetOrArg &RA);
void MarkLive(const Function &F);
void PropagateLiveness(const RetOrArg &RA);
bool RemoveDeadStuffFromFunction(Function *F);
bool DeleteDeadVarargs(Function &Fn);
bool RemoveDeadArgumentsFromCallers(Function &Fn);
};
}
#endif // LLVM_TRANSFORMS_IPO_DEADARGUMENTELIMINATION_H

View File

@ -52,6 +52,7 @@
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/GCOVProfiler.h"
#include "llvm/Transforms/IPO/ConstantMerge.h"
#include "llvm/Transforms/IPO/DeadArgumentElimination.h"
#include "llvm/Transforms/IPO/ElimAvailExtern.h"
#include "llvm/Transforms/IPO/ForceFunctionAttrs.h"
#include "llvm/Transforms/IPO/FunctionAttrs.h"

View File

@ -38,6 +38,7 @@ MODULE_ALIAS_ANALYSIS("globals-aa", GlobalsAA())
#define MODULE_PASS(NAME, CREATE_PASS)
#endif
MODULE_PASS("constmerge", ConstantMergePass())
MODULE_PASS("deadargelim", DeadArgumentEliminationPass())
MODULE_PASS("elim-avail-extern", EliminateAvailableExternallyPass())
MODULE_PASS("forceattrs", ForceFunctionAttrsPass())
MODULE_PASS("globaldce", GlobalDCEPass())

View File

@ -17,6 +17,7 @@
//
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/IPO/DeadArgumentElimination.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
@ -49,77 +50,6 @@ namespace {
/// DAE - The dead argument elimination pass.
///
class DAE : public ModulePass {
public:
/// Struct that represents (part of) either a return value or a function
/// argument. Used so that arguments and return values can be used
/// interchangeably.
struct RetOrArg {
RetOrArg(const Function *F, unsigned Idx, bool IsArg) : F(F), Idx(Idx),
IsArg(IsArg) {}
const Function *F;
unsigned Idx;
bool IsArg;
/// Make RetOrArg comparable, so we can put it into a map.
bool operator<(const RetOrArg &O) const {
return std::tie(F, Idx, IsArg) < std::tie(O.F, O.Idx, O.IsArg);
}
/// Make RetOrArg comparable, so we can easily iterate the multimap.
bool operator==(const RetOrArg &O) const {
return F == O.F && Idx == O.Idx && IsArg == O.IsArg;
}
std::string getDescription() const {
return (Twine(IsArg ? "Argument #" : "Return value #") + Twine(Idx) +
" of function " + F->getName()).str();
}
};
/// Liveness enum - During our initial pass over the program, we determine
/// that things are either alive or maybe alive. We don't mark anything
/// explicitly dead (even if we know they are), since anything not alive
/// with no registered uses (in Uses) will never be marked alive and will
/// thus become dead in the end.
enum Liveness { Live, MaybeLive };
/// Convenience wrapper
RetOrArg CreateRet(const Function *F, unsigned Idx) {
return RetOrArg(F, Idx, false);
}
/// Convenience wrapper
RetOrArg CreateArg(const Function *F, unsigned Idx) {
return RetOrArg(F, Idx, true);
}
typedef std::multimap<RetOrArg, RetOrArg> UseMap;
/// This maps a return value or argument to any MaybeLive return values or
/// arguments it uses. This allows the MaybeLive values to be marked live
/// when any of its users is marked live.
/// For example (indices are left out for clarity):
/// - Uses[ret F] = ret G
/// This means that F calls G, and F returns the value returned by G.
/// - Uses[arg F] = ret G
/// This means that some function calls G and passes its result as an
/// argument to F.
/// - Uses[ret F] = arg F
/// This means that F returns one of its own arguments.
/// - Uses[arg F] = arg G
/// This means that G calls F and passes one of its own (G's) arguments
/// directly to F.
UseMap Uses;
typedef std::set<RetOrArg> LiveSet;
typedef std::set<const Function*> LiveFuncSet;
/// This set contains all values that have been determined to be live.
LiveSet LiveValues;
/// This set contains all values that are cannot be changed in any way.
LiveFuncSet LiveFunctions;
typedef SmallVector<RetOrArg, 5> UseVector;
protected:
// DAH uses this to specify a different ID.
explicit DAE(char &ID) : ModulePass(ID) {}
@ -130,25 +60,15 @@ namespace {
initializeDAEPass(*PassRegistry::getPassRegistry());
}
bool runOnModule(Module &M) override;
bool runOnModule(Module &M) override {
if (skipModule(M))
return false;
DeadArgumentEliminationPass DAEP(ShouldHackArguments());
PreservedAnalyses PA = DAEP.run(M);
return !PA.areAllPreserved();
}
virtual bool ShouldHackArguments() const { return false; }
private:
Liveness MarkIfNotLive(RetOrArg Use, UseVector &MaybeLiveUses);
Liveness SurveyUse(const Use *U, UseVector &MaybeLiveUses,
unsigned RetValNum = -1U);
Liveness SurveyUses(const Value *V, UseVector &MaybeLiveUses);
void SurveyFunction(const Function &F);
void MarkValue(const RetOrArg &RA, Liveness L,
const UseVector &MaybeLiveUses);
void MarkLive(const RetOrArg &RA);
void MarkLive(const Function &F);
void PropagateLiveness(const RetOrArg &RA);
bool RemoveDeadStuffFromFunction(Function *F);
bool DeleteDeadVarargs(Function &Fn);
bool RemoveDeadArgumentsFromCallers(Function &Fn);
};
}
@ -181,7 +101,7 @@ ModulePass *llvm::createDeadArgHackingPass() { return new DAH(); }
/// DeleteDeadVarargs - If this is an function that takes a ... list, and if
/// llvm.vastart is never called, the varargs list is dead for the function.
bool DAE::DeleteDeadVarargs(Function &Fn) {
bool DeadArgumentEliminationPass::DeleteDeadVarargs(Function &Fn) {
assert(Fn.getFunctionType()->isVarArg() && "Function isn't varargs!");
if (Fn.isDeclaration() || !Fn.hasLocalLinkage()) return false;
@ -317,8 +237,7 @@ bool DAE::DeleteDeadVarargs(Function &Fn) {
/// RemoveDeadArgumentsFromCallers - Checks if the given function has any
/// arguments that are unused, and changes the caller parameters to be undefined
/// instead.
bool DAE::RemoveDeadArgumentsFromCallers(Function &Fn)
{
bool DeadArgumentEliminationPass::RemoveDeadArgumentsFromCallers(Function &Fn) {
// We cannot change the arguments if this TU does not define the function or
// if the linker may choose a function body from another TU, even if the
// nominal linkage indicates that other copies of the function have the same
@ -410,7 +329,9 @@ static Type *getRetComponentType(const Function *F, unsigned Idx) {
/// MarkIfNotLive - This checks Use for liveness in LiveValues. If Use is not
/// live, it adds Use to the MaybeLiveUses argument. Returns the determined
/// liveness of Use.
DAE::Liveness DAE::MarkIfNotLive(RetOrArg Use, UseVector &MaybeLiveUses) {
DeadArgumentEliminationPass::Liveness
DeadArgumentEliminationPass::MarkIfNotLive(RetOrArg Use,
UseVector &MaybeLiveUses) {
// We're live if our use or its Function is already marked as live.
if (LiveFunctions.count(Use.F) || LiveValues.count(Use))
return Live;
@ -429,8 +350,9 @@ DAE::Liveness DAE::MarkIfNotLive(RetOrArg Use, UseVector &MaybeLiveUses) {
/// RetValNum is the return value number to use when this use is used in a
/// return instruction. This is used in the recursion, you should always leave
/// it at 0.
DAE::Liveness DAE::SurveyUse(const Use *U,
UseVector &MaybeLiveUses, unsigned RetValNum) {
DeadArgumentEliminationPass::Liveness
DeadArgumentEliminationPass::SurveyUse(const Use *U, UseVector &MaybeLiveUses,
unsigned RetValNum) {
const User *V = U->getUser();
if (const ReturnInst *RI = dyn_cast<ReturnInst>(V)) {
// The value is returned from a function. It's only live when the
@ -443,13 +365,14 @@ DAE::Liveness DAE::SurveyUse(const Use *U,
// We might be live, depending on the liveness of Use.
return MarkIfNotLive(Use, MaybeLiveUses);
} else {
DAE::Liveness Result = MaybeLive;
DeadArgumentEliminationPass::Liveness Result = MaybeLive;
for (unsigned i = 0; i < NumRetVals(F); ++i) {
RetOrArg Use = CreateRet(F, i);
// We might be live, depending on the liveness of Use. If any
// sub-value is live, then the entire value is considered live. This
// is a conservative choice, and better tracking is possible.
DAE::Liveness SubResult = MarkIfNotLive(Use, MaybeLiveUses);
DeadArgumentEliminationPass::Liveness SubResult =
MarkIfNotLive(Use, MaybeLiveUses);
if (Result != Live)
Result = SubResult;
}
@ -515,7 +438,9 @@ DAE::Liveness DAE::SurveyUse(const Use *U,
/// Adds all uses that cause the result to be MaybeLive to MaybeLiveRetUses. If
/// the result is Live, MaybeLiveUses might be modified but its content should
/// be ignored (since it might not be complete).
DAE::Liveness DAE::SurveyUses(const Value *V, UseVector &MaybeLiveUses) {
DeadArgumentEliminationPass::Liveness
DeadArgumentEliminationPass::SurveyUses(const Value *V,
UseVector &MaybeLiveUses) {
// Assume it's dead (which will only hold if there are no uses at all..).
Liveness Result = MaybeLive;
// Check each use.
@ -535,7 +460,7 @@ DAE::Liveness DAE::SurveyUses(const Value *V, UseVector &MaybeLiveUses) {
// We consider arguments of non-internal functions to be intrinsically alive as
// well as arguments to functions which have their "address taken".
//
void DAE::SurveyFunction(const Function &F) {
void DeadArgumentEliminationPass::SurveyFunction(const Function &F) {
// Functions with inalloca parameters are expecting args in a particular
// register and memory layout.
if (F.getAttributes().hasAttrSomewhere(Attribute::InAlloca)) {
@ -571,12 +496,13 @@ void DAE::SurveyFunction(const Function &F) {
return;
}
if (!F.hasLocalLinkage() && (!ShouldHackArguments() || F.isIntrinsic())) {
if (!F.hasLocalLinkage() && (!ShouldHackArguments || F.isIntrinsic())) {
MarkLive(F);
return;
}
DEBUG(dbgs() << "DAE - Inspecting callers for fn: " << F.getName() << "\n");
DEBUG(dbgs() << "DeadArgumentEliminationPass - Inspecting callers for fn: "
<< F.getName() << "\n");
// Keep track of the number of live retvals, so we can skip checks once all
// of them turn out to be live.
unsigned NumLiveRetVals = 0;
@ -638,7 +564,8 @@ void DAE::SurveyFunction(const Function &F) {
for (unsigned i = 0; i != RetCount; ++i)
MarkValue(CreateRet(&F, i), RetValLiveness[i], MaybeLiveRetUses[i]);
DEBUG(dbgs() << "DAE - Inspecting args for fn: " << F.getName() << "\n");
DEBUG(dbgs() << "DeadArgumentEliminationPass - Inspecting args for fn: "
<< F.getName() << "\n");
// Now, check all of our arguments.
unsigned i = 0;
@ -670,8 +597,8 @@ void DAE::SurveyFunction(const Function &F) {
/// MaybeLive, it also takes all uses in MaybeLiveUses and records them in Uses,
/// such that RA will be marked live if any use in MaybeLiveUses gets marked
/// live later on.
void DAE::MarkValue(const RetOrArg &RA, Liveness L,
const UseVector &MaybeLiveUses) {
void DeadArgumentEliminationPass::MarkValue(const RetOrArg &RA, Liveness L,
const UseVector &MaybeLiveUses) {
switch (L) {
case Live: MarkLive(RA); break;
case MaybeLive:
@ -690,8 +617,9 @@ void DAE::MarkValue(const RetOrArg &RA, Liveness L,
/// changed in any way. Additionally,
/// mark any values that are used as this function's parameters or by its return
/// values (according to Uses) live as well.
void DAE::MarkLive(const Function &F) {
DEBUG(dbgs() << "DAE - Intrinsically live fn: " << F.getName() << "\n");
void DeadArgumentEliminationPass::MarkLive(const Function &F) {
DEBUG(dbgs() << "DeadArgumentEliminationPass - Intrinsically live fn: "
<< F.getName() << "\n");
// Mark the function as live.
LiveFunctions.insert(&F);
// Mark all arguments as live.
@ -705,20 +633,21 @@ void DAE::MarkLive(const Function &F) {
/// MarkLive - Mark the given return value or argument as live. Additionally,
/// mark any values that are used by this value (according to Uses) live as
/// well.
void DAE::MarkLive(const RetOrArg &RA) {
void DeadArgumentEliminationPass::MarkLive(const RetOrArg &RA) {
if (LiveFunctions.count(RA.F))
return; // Function was already marked Live.
if (!LiveValues.insert(RA).second)
return; // We were already marked Live.
DEBUG(dbgs() << "DAE - Marking " << RA.getDescription() << " live\n");
DEBUG(dbgs() << "DeadArgumentEliminationPass - Marking "
<< RA.getDescription() << " live\n");
PropagateLiveness(RA);
}
/// PropagateLiveness - Given that RA is a live value, propagate it's liveness
/// to any other values it uses (according to Uses).
void DAE::PropagateLiveness(const RetOrArg &RA) {
void DeadArgumentEliminationPass::PropagateLiveness(const RetOrArg &RA) {
// We don't use upper_bound (or equal_range) here, because our recursive call
// to ourselves is likely to cause the upper_bound (which is the first value
// not belonging to RA) to become erased and the iterator invalidated.
@ -737,7 +666,7 @@ void DAE::PropagateLiveness(const RetOrArg &RA) {
// that are not in LiveValues. Transform the function and all of the callees of
// the function to not have these arguments and return values.
//
bool DAE::RemoveDeadStuffFromFunction(Function *F) {
bool DeadArgumentEliminationPass::RemoveDeadStuffFromFunction(Function *F) {
// Don't modify fully live functions
if (LiveFunctions.count(F))
return false;
@ -778,8 +707,9 @@ bool DAE::RemoveDeadStuffFromFunction(Function *F) {
}
} else {
++NumArgumentsEliminated;
DEBUG(dbgs() << "DAE - Removing argument " << i << " (" << I->getName()
<< ") from " << F->getName() << "\n");
DEBUG(dbgs() << "DeadArgumentEliminationPass - Removing argument " << i
<< " (" << I->getName() << ") from " << F->getName()
<< "\n");
}
}
@ -822,8 +752,8 @@ bool DAE::RemoveDeadStuffFromFunction(Function *F) {
NewRetIdxs[i] = RetTypes.size() - 1;
} else {
++NumRetValsEliminated;
DEBUG(dbgs() << "DAE - Removing return value " << i << " from "
<< F->getName() << "\n");
DEBUG(dbgs() << "DeadArgumentEliminationPass - Removing return value "
<< i << " from " << F->getName() << "\n");
}
}
if (RetTypes.size() > 1) {
@ -1097,17 +1027,14 @@ bool DAE::RemoveDeadStuffFromFunction(Function *F) {
return true;
}
bool DAE::runOnModule(Module &M) {
if (skipModule(M))
return false;
PreservedAnalyses DeadArgumentEliminationPass::run(Module &M) {
bool Changed = false;
// First pass: Do a simple check to see if any functions can have their "..."
// removed. We can do this if they never call va_start. This loop cannot be
// fused with the next loop, because deleting a function invalidates
// information computed while surveying other functions.
DEBUG(dbgs() << "DAE - Deleting dead varargs\n");
DEBUG(dbgs() << "DeadArgumentEliminationPass - Deleting dead varargs\n");
for (Module::iterator I = M.begin(), E = M.end(); I != E; ) {
Function &F = *I++;
if (F.getFunctionType()->isVarArg())
@ -1118,7 +1045,7 @@ bool DAE::runOnModule(Module &M) {
// We assume all arguments are dead unless proven otherwise (allowing us to
// determine that dead arguments passed into recursive functions are dead).
//
DEBUG(dbgs() << "DAE - Determining liveness\n");
DEBUG(dbgs() << "DeadArgumentEliminationPass - Determining liveness\n");
for (auto &F : M)
SurveyFunction(F);
@ -1136,5 +1063,7 @@ bool DAE::runOnModule(Module &M) {
for (auto &F : M)
Changed |= RemoveDeadArgumentsFromCallers(F);
return Changed;
if (!Changed)
return PreservedAnalyses::all();
return PreservedAnalyses::none();
}

View File

@ -1,4 +1,4 @@
; RUN: opt < %s -deadargelim -S | FileCheck %s
; RUN: opt < %s -passes=deadargelim -S | FileCheck %s
%Ty = type { i32, i32 }