mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-24 12:19:53 +00:00
bugpoint Enhancement.
Summary: This patch adds two flags to `bugpoint`: "-replace-funcs-with-null" and "-disable-pass-list-reduction". When "-replace-funcs-with-null" is specified, bugpoint will, instead of simply deleting function bodies, replace all uses of functions and then will delete functions completely from the test module, correctly handling aliasing and @llvm.used && @llvm.compiler.used. This part was conceived while trying to debug the PNaCl IR simplification passes, which don't allow undefined functions (ie no declarations). With "-disable-pass-list-reduction", bugpoint won't try to reduce the set of passes causing the "crash". This is needed in cases where one is trying to debug an issue inside the PNaCl IR simplification passes which is causing an PNaCl ABI verification error, for example. Reviewers: jfb Reviewed By: jfb Subscribers: jfb, llvm-commits Differential Revision: http://reviews.llvm.org/D8555 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@235362 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
af337cd20e
commit
7b862ec88e
17
test/BugPoint/replace-funcs-with-null.ll
Normal file
17
test/BugPoint/replace-funcs-with-null.ll
Normal file
@ -0,0 +1,17 @@
|
||||
; Test that bugpoint can reduce the set of functions by replacing them with null.
|
||||
;
|
||||
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%shlibext %s -output-prefix %t -replace-funcs-with-null -bugpoint-crash-decl-funcs -silence-passes -safe-run-llc
|
||||
; REQUIRES: loadable_module
|
||||
|
||||
@foo2 = alias i32 ()* @foo
|
||||
|
||||
define i32 @foo() { ret i32 1 }
|
||||
|
||||
define i32 @test() {
|
||||
call i32 @test()
|
||||
ret i32 %1
|
||||
}
|
||||
|
||||
define i32 @bar() { ret i32 2 }
|
||||
|
||||
@llvm.used = appending global [1 x i8*] [i8* bitcast (i32 ()* @foo to i8*)], section "llvm.metadata"
|
@ -68,8 +68,32 @@ namespace {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
char DeleteCalls::ID = 0;
|
||||
static RegisterPass<DeleteCalls>
|
||||
Y("bugpoint-deletecalls",
|
||||
"BugPoint Test Pass - Intentionally 'misoptimize' CallInsts");
|
||||
|
||||
namespace {
|
||||
/// CrashOnDeclFunc - This pass is used to test bugpoint. It intentionally
|
||||
/// crash if the module has an undefined function (ie a function that is
|
||||
/// defined in an external module).
|
||||
class CrashOnDeclFunc : public ModulePass {
|
||||
public:
|
||||
static char ID; // Pass ID, replacement for typeid
|
||||
CrashOnDeclFunc() : ModulePass(ID) {}
|
||||
private:
|
||||
bool runOnModule(Module &M) override {
|
||||
for (auto &F : M.functions()) {
|
||||
if (F.isDeclaration())
|
||||
abort();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
char CrashOnDeclFunc::ID = 0;
|
||||
static RegisterPass<CrashOnDeclFunc>
|
||||
Z("bugpoint-crash-decl-funcs",
|
||||
"BugPoint Test Pass - Intentionally crash on declared functions");
|
||||
|
@ -40,6 +40,15 @@ namespace {
|
||||
NoGlobalRM ("disable-global-remove",
|
||||
cl::desc("Do not remove global variables"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool>
|
||||
ReplaceFuncsWithNull("replace-funcs-with-null",
|
||||
cl::desc("When stubbing functions, replace all uses will null"),
|
||||
cl::init(false));
|
||||
cl::opt<bool>
|
||||
DontReducePassList("disable-pass-list-reduction",
|
||||
cl::desc("Skip pass list reduction steps"),
|
||||
cl::init(false));
|
||||
}
|
||||
|
||||
namespace llvm {
|
||||
@ -194,6 +203,29 @@ namespace {
|
||||
};
|
||||
}
|
||||
|
||||
static void RemoveFunctionReferences(Module *M, const char* Name) {
|
||||
auto *UsedVar = M->getGlobalVariable(Name, true);
|
||||
if (!UsedVar || !UsedVar->hasInitializer()) return;
|
||||
if (isa<ConstantAggregateZero>(UsedVar->getInitializer())) {
|
||||
assert(UsedVar->use_empty());
|
||||
UsedVar->eraseFromParent();
|
||||
return;
|
||||
}
|
||||
auto *OldUsedVal = cast<ConstantArray>(UsedVar->getInitializer());
|
||||
std::vector<Constant*> Used;
|
||||
for(Value *V : OldUsedVal->operand_values()) {
|
||||
Constant *Op = cast<Constant>(V->stripPointerCasts());
|
||||
if(!Op->isNullValue()) {
|
||||
Used.push_back(cast<Constant>(V));
|
||||
}
|
||||
}
|
||||
auto *NewValElemTy = OldUsedVal->getType()->getElementType();
|
||||
auto *NewValTy = ArrayType::get(NewValElemTy, Used.size());
|
||||
auto *NewUsedVal = ConstantArray::get(NewValTy, Used);
|
||||
UsedVar->mutateType(NewUsedVal->getType()->getPointerTo());
|
||||
UsedVar->setInitializer(NewUsedVal);
|
||||
}
|
||||
|
||||
bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) {
|
||||
// If main isn't present, claim there is no problem.
|
||||
if (KeepMain && std::find(Funcs.begin(), Funcs.end(),
|
||||
@ -218,13 +250,53 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) {
|
||||
outs() << "Checking for crash with only these functions: ";
|
||||
PrintFunctionList(Funcs);
|
||||
outs() << ": ";
|
||||
if (!ReplaceFuncsWithNull) {
|
||||
// Loop over and delete any functions which we aren't supposed to be playing
|
||||
// with...
|
||||
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
|
||||
if (!I->isDeclaration() && !Functions.count(I))
|
||||
DeleteFunctionBody(I);
|
||||
} else {
|
||||
std::vector<GlobalValue*> ToRemove;
|
||||
// First, remove aliases to functions we're about to purge.
|
||||
for (GlobalAlias &Alias : M->aliases()) {
|
||||
Constant *Root = Alias.getAliasee()->stripPointerCasts();
|
||||
Function *F = dyn_cast<Function>(Root);
|
||||
if (F) {
|
||||
if (Functions.count(F))
|
||||
// We're keeping this function.
|
||||
continue;
|
||||
} else if (Root->isNullValue()) {
|
||||
// This referenced a globalalias that we've already replaced,
|
||||
// so we still need to replace this alias.
|
||||
} else if (!F) {
|
||||
// Not a function, therefore not something we mess with.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Loop over and delete any functions which we aren't supposed to be playing
|
||||
// with...
|
||||
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
|
||||
if (!I->isDeclaration() && !Functions.count(I))
|
||||
DeleteFunctionBody(I);
|
||||
PointerType *Ty = cast<PointerType>(Alias.getType());
|
||||
Constant *Replacement = ConstantPointerNull::get(Ty);
|
||||
Alias.replaceAllUsesWith(Replacement);
|
||||
ToRemove.push_back(&Alias);
|
||||
}
|
||||
|
||||
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) {
|
||||
if (!I->isDeclaration() && !Functions.count(I)) {
|
||||
PointerType *Ty = cast<PointerType>(I->getType());
|
||||
Constant *Replacement = ConstantPointerNull::get(Ty);
|
||||
I->replaceAllUsesWith(Replacement);
|
||||
ToRemove.push_back(I);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto *F : ToRemove) {
|
||||
F->eraseFromParent();
|
||||
}
|
||||
|
||||
// Finally, remove any null members from any global intrinsic.
|
||||
RemoveFunctionReferences(M, "llvm.used");
|
||||
RemoveFunctionReferences(M, "llvm.compiler.used");
|
||||
}
|
||||
// Try running the hacked up program...
|
||||
if (TestFn(BD, M)) {
|
||||
BD.setNewProgram(M); // It crashed, keep the trimmed version...
|
||||
@ -296,7 +368,7 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock*> &BBs) {
|
||||
(*SI)->removePredecessor(BB);
|
||||
|
||||
TerminatorInst *BBTerm = BB->getTerminator();
|
||||
|
||||
|
||||
if (!BB->getTerminator()->getType()->isVoidTy())
|
||||
BBTerm->replaceAllUsesWith(Constant::getNullValue(BBTerm->getType()));
|
||||
|
||||
@ -629,7 +701,7 @@ bool BugDriver::debugOptimizerCrash(const std::string &ID) {
|
||||
|
||||
std::string Error;
|
||||
// Reduce the list of passes which causes the optimizer to crash...
|
||||
if (!BugpointIsInterrupted)
|
||||
if (!BugpointIsInterrupted && !DontReducePassList)
|
||||
ReducePassList(*this).reduceList(PassesToRun, Error);
|
||||
assert(Error.empty());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user