Index: test/BugPoint/replace-funcs-with-null.ll =================================================================== --- /dev/null +++ test/BugPoint/replace-funcs-with-null.ll @@ -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" Index: tools/bugpoint-passes/TestPasses.cpp =================================================================== --- tools/bugpoint-passes/TestPasses.cpp +++ tools/bugpoint-passes/TestPasses.cpp @@ -68,8 +68,32 @@ } }; } - + char DeleteCalls::ID = 0; static RegisterPass 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 + Z("bugpoint-crash-decl-funcs", + "BugPoint Test Pass - Intentionally crash on declared functions"); Index: tools/bugpoint/CrashDebugger.cpp =================================================================== --- tools/bugpoint/CrashDebugger.cpp +++ tools/bugpoint/CrashDebugger.cpp @@ -40,6 +40,15 @@ NoGlobalRM ("disable-global-remove", cl::desc("Do not remove global variables"), cl::init(false)); + + cl::opt + ReplaceFuncsWithNull("replace-funcs-with-null", + cl::desc("When stubbing functions, replace all uses will null"), + cl::init(false)); + cl::opt + DontReducePassList("disable-pass-list-reduction", + cl::desc("Skip pass list reduction steps"), + cl::init(false)); } namespace llvm { @@ -194,6 +203,29 @@ }; } +static void RemoveFunctionReferences(Module *M, const char* Name) { + auto *UsedVar = M->getGlobalVariable(Name, true); + if (!UsedVar || !UsedVar->hasInitializer()) return; + if (isa(UsedVar->getInitializer())) { + assert(UsedVar->use_empty()); + UsedVar->eraseFromParent(); + return; + } + auto *OldUsedVal = cast(UsedVar->getInitializer()); + std::vector Used; + for(Value *V : OldUsedVal->operand_values()) { + Constant *Op = cast(V->stripPointerCasts()); + if(!Op->isNullValue()) { + Used.push_back(cast(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 &Funcs) { // If main isn't present, claim there is no problem. if (KeepMain && std::find(Funcs.begin(), Funcs.end(), @@ -218,13 +250,53 @@ 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 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(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(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(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 @@ (*SI)->removePredecessor(BB); TerminatorInst *BBTerm = BB->getTerminator(); - + if (!BB->getTerminator()->getType()->isVoidTy()) BBTerm->replaceAllUsesWith(Constant::getNullValue(BBTerm->getType())); @@ -629,7 +701,7 @@ 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());