Skip to content

Commit f87e20d

Browse files
committed
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 llvm-svn: 235362
1 parent 362f89c commit f87e20d

File tree

3 files changed

+121
-8
lines changed

3 files changed

+121
-8
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
; Test that bugpoint can reduce the set of functions by replacing them with null.
2+
;
3+
; RUN: bugpoint -load %llvmshlibdir/BugpointPasses%shlibext %s -output-prefix %t -replace-funcs-with-null -bugpoint-crash-decl-funcs -silence-passes -safe-run-llc
4+
; REQUIRES: loadable_module
5+
6+
@foo2 = alias i32 ()* @foo
7+
8+
define i32 @foo() { ret i32 1 }
9+
10+
define i32 @test() {
11+
call i32 @test()
12+
ret i32 %1
13+
}
14+
15+
define i32 @bar() { ret i32 2 }
16+
17+
@llvm.used = appending global [1 x i8*] [i8* bitcast (i32 ()* @foo to i8*)], section "llvm.metadata"

llvm/tools/bugpoint-passes/TestPasses.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,32 @@ namespace {
6868
}
6969
};
7070
}
71-
71+
7272
char DeleteCalls::ID = 0;
7373
static RegisterPass<DeleteCalls>
7474
Y("bugpoint-deletecalls",
7575
"BugPoint Test Pass - Intentionally 'misoptimize' CallInsts");
76+
77+
namespace {
78+
/// CrashOnDeclFunc - This pass is used to test bugpoint. It intentionally
79+
/// crash if the module has an undefined function (ie a function that is
80+
/// defined in an external module).
81+
class CrashOnDeclFunc : public ModulePass {
82+
public:
83+
static char ID; // Pass ID, replacement for typeid
84+
CrashOnDeclFunc() : ModulePass(ID) {}
85+
private:
86+
bool runOnModule(Module &M) override {
87+
for (auto &F : M.functions()) {
88+
if (F.isDeclaration())
89+
abort();
90+
}
91+
return false;
92+
}
93+
};
94+
}
95+
96+
char CrashOnDeclFunc::ID = 0;
97+
static RegisterPass<CrashOnDeclFunc>
98+
Z("bugpoint-crash-decl-funcs",
99+
"BugPoint Test Pass - Intentionally crash on declared functions");

llvm/tools/bugpoint/CrashDebugger.cpp

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ namespace {
4040
NoGlobalRM ("disable-global-remove",
4141
cl::desc("Do not remove global variables"),
4242
cl::init(false));
43+
44+
cl::opt<bool>
45+
ReplaceFuncsWithNull("replace-funcs-with-null",
46+
cl::desc("When stubbing functions, replace all uses will null"),
47+
cl::init(false));
48+
cl::opt<bool>
49+
DontReducePassList("disable-pass-list-reduction",
50+
cl::desc("Skip pass list reduction steps"),
51+
cl::init(false));
4352
}
4453

4554
namespace llvm {
@@ -194,6 +203,29 @@ namespace {
194203
};
195204
}
196205

206+
static void RemoveFunctionReferences(Module *M, const char* Name) {
207+
auto *UsedVar = M->getGlobalVariable(Name, true);
208+
if (!UsedVar || !UsedVar->hasInitializer()) return;
209+
if (isa<ConstantAggregateZero>(UsedVar->getInitializer())) {
210+
assert(UsedVar->use_empty());
211+
UsedVar->eraseFromParent();
212+
return;
213+
}
214+
auto *OldUsedVal = cast<ConstantArray>(UsedVar->getInitializer());
215+
std::vector<Constant*> Used;
216+
for(Value *V : OldUsedVal->operand_values()) {
217+
Constant *Op = cast<Constant>(V->stripPointerCasts());
218+
if(!Op->isNullValue()) {
219+
Used.push_back(cast<Constant>(V));
220+
}
221+
}
222+
auto *NewValElemTy = OldUsedVal->getType()->getElementType();
223+
auto *NewValTy = ArrayType::get(NewValElemTy, Used.size());
224+
auto *NewUsedVal = ConstantArray::get(NewValTy, Used);
225+
UsedVar->mutateType(NewUsedVal->getType()->getPointerTo());
226+
UsedVar->setInitializer(NewUsedVal);
227+
}
228+
197229
bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) {
198230
// If main isn't present, claim there is no problem.
199231
if (KeepMain && std::find(Funcs.begin(), Funcs.end(),
@@ -218,13 +250,53 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) {
218250
outs() << "Checking for crash with only these functions: ";
219251
PrintFunctionList(Funcs);
220252
outs() << ": ";
253+
if (!ReplaceFuncsWithNull) {
254+
// Loop over and delete any functions which we aren't supposed to be playing
255+
// with...
256+
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
257+
if (!I->isDeclaration() && !Functions.count(I))
258+
DeleteFunctionBody(I);
259+
} else {
260+
std::vector<GlobalValue*> ToRemove;
261+
// First, remove aliases to functions we're about to purge.
262+
for (GlobalAlias &Alias : M->aliases()) {
263+
Constant *Root = Alias.getAliasee()->stripPointerCasts();
264+
Function *F = dyn_cast<Function>(Root);
265+
if (F) {
266+
if (Functions.count(F))
267+
// We're keeping this function.
268+
continue;
269+
} else if (Root->isNullValue()) {
270+
// This referenced a globalalias that we've already replaced,
271+
// so we still need to replace this alias.
272+
} else if (!F) {
273+
// Not a function, therefore not something we mess with.
274+
continue;
275+
}
221276

222-
// Loop over and delete any functions which we aren't supposed to be playing
223-
// with...
224-
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
225-
if (!I->isDeclaration() && !Functions.count(I))
226-
DeleteFunctionBody(I);
277+
PointerType *Ty = cast<PointerType>(Alias.getType());
278+
Constant *Replacement = ConstantPointerNull::get(Ty);
279+
Alias.replaceAllUsesWith(Replacement);
280+
ToRemove.push_back(&Alias);
281+
}
282+
283+
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) {
284+
if (!I->isDeclaration() && !Functions.count(I)) {
285+
PointerType *Ty = cast<PointerType>(I->getType());
286+
Constant *Replacement = ConstantPointerNull::get(Ty);
287+
I->replaceAllUsesWith(Replacement);
288+
ToRemove.push_back(I);
289+
}
290+
}
227291

292+
for (auto *F : ToRemove) {
293+
F->eraseFromParent();
294+
}
295+
296+
// Finally, remove any null members from any global intrinsic.
297+
RemoveFunctionReferences(M, "llvm.used");
298+
RemoveFunctionReferences(M, "llvm.compiler.used");
299+
}
228300
// Try running the hacked up program...
229301
if (TestFn(BD, M)) {
230302
BD.setNewProgram(M); // It crashed, keep the trimmed version...
@@ -296,7 +368,7 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock*> &BBs) {
296368
(*SI)->removePredecessor(BB);
297369

298370
TerminatorInst *BBTerm = BB->getTerminator();
299-
371+
300372
if (!BB->getTerminator()->getType()->isVoidTy())
301373
BBTerm->replaceAllUsesWith(Constant::getNullValue(BBTerm->getType()));
302374

@@ -629,7 +701,7 @@ bool BugDriver::debugOptimizerCrash(const std::string &ID) {
629701

630702
std::string Error;
631703
// Reduce the list of passes which causes the optimizer to crash...
632-
if (!BugpointIsInterrupted)
704+
if (!BugpointIsInterrupted && !DontReducePassList)
633705
ReducePassList(*this).reduceList(PassesToRun, Error);
634706
assert(Error.empty());
635707

0 commit comments

Comments
 (0)