/// DebugACrash - Given a predicate that determines whether a component crashes /// on a program, try to destructively reduce the program while still keeping /// the predicate true. static Error DebugACrash(BugDriver &BD, bool (*TestFn)(const BugDriver &, Module *)) { // See if we can get away with nuking some of the global variable initializers // in the program... if (!NoGlobalRM) if (Error E = ReduceGlobalInitializers(BD, TestFn)) return E; // Now try to reduce the number of functions in the module to something small. std::vector<Function *> Functions; for (Function &F : *BD.getProgram()) if (!F.isDeclaration()) Functions.push_back(&F); if (Functions.size() > 1 && !BugpointIsInterrupted) { outs() << "\n*** Attempting to reduce the number of functions " "in the testcase\n"; unsigned OldSize = Functions.size(); Expected<bool> Result = ReduceCrashingFunctions(BD, TestFn).reduceList(Functions); if (Error E = Result.takeError()) return E; if (Functions.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-function"); } // Attempt to change conditional branches into unconditional branches to // eliminate blocks. if (!DisableSimplifyCFG && !BugpointIsInterrupted) { std::vector<const BasicBlock *> Blocks; for (Function &F : *BD.getProgram()) for (BasicBlock &BB : F) Blocks.push_back(&BB); unsigned OldSize = Blocks.size(); Expected<bool> Result = ReduceCrashingConditionals(BD, TestFn, true).reduceList(Blocks); if (Error E = Result.takeError()) return E; Result = ReduceCrashingConditionals(BD, TestFn, false).reduceList(Blocks); if (Error E = Result.takeError()) return E; if (Blocks.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-conditionals"); } // Attempt to delete entire basic blocks at a time to speed up // convergence... this actually works by setting the terminator of the blocks // to a return instruction then running simplifycfg, which can potentially // shrinks the code dramatically quickly // if (!DisableSimplifyCFG && !BugpointIsInterrupted) { std::vector<const BasicBlock *> Blocks; for (Function &F : *BD.getProgram()) for (BasicBlock &BB : F) Blocks.push_back(&BB); unsigned OldSize = Blocks.size(); Expected<bool> Result = ReduceCrashingBlocks(BD, TestFn).reduceList(Blocks); if (Error E = Result.takeError()) return E; if (Blocks.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-blocks"); } if (!DisableSimplifyCFG & !BugpointIsInterrupted) { std::vector<const BasicBlock *> Blocks; for (Function &F : *BD.getProgram()) for (BasicBlock &BB : F) Blocks.push_back(&BB); unsigned OldSize = Blocks.size(); Expected<bool> Result = ReduceSimplifyCFG(BD, TestFn).reduceList(Blocks); if (Error E = Result.takeError()) return E; if (Blocks.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-simplifycfg"); } // Attempt to delete instructions using bisection. This should help out nasty // cases with large basic blocks where the problem is at one end. if (!BugpointIsInterrupted) if (Error E = ReduceInsts(BD, TestFn)) return E; // Attempt to strip debug info metadata. auto stripMetadata = [&](std::function<bool(Module &)> strip) { std::unique_ptr<Module> M = CloneModule(BD.getProgram()); strip(*M); if (TestFn(BD, M.get())) BD.setNewProgram(M.release()); }; if (!NoStripDebugInfo && !BugpointIsInterrupted) { outs() << "\n*** Attempting to strip the debug info: "; stripMetadata(StripDebugInfo); } if (!NoStripDebugTypeInfo && !BugpointIsInterrupted) { outs() << "\n*** Attempting to strip the debug type info: "; stripMetadata(stripNonLineTableDebugInfo); } if (!NoNamedMDRM) { if (!BugpointIsInterrupted) { // Try to reduce the amount of global metadata (particularly debug info), // by dropping global named metadata that anchors them outs() << "\n*** Attempting to remove named metadata: "; std::vector<std::string> NamedMDNames; for (auto &NamedMD : BD.getProgram()->named_metadata()) NamedMDNames.push_back(NamedMD.getName().str()); Expected<bool> Result = ReduceCrashingNamedMD(BD, TestFn).reduceList(NamedMDNames); if (Error E = Result.takeError()) return E; } if (!BugpointIsInterrupted) { // Now that we quickly dropped all the named metadata that doesn't // contribute to the crash, bisect the operands of the remaining ones std::vector<const MDNode *> NamedMDOps; for (auto &NamedMD : BD.getProgram()->named_metadata()) for (auto op : NamedMD.operands()) NamedMDOps.push_back(op); Expected<bool> Result = ReduceCrashingNamedMDOps(BD, TestFn).reduceList(NamedMDOps); if (Error E = Result.takeError()) return E; } BD.EmitProgressBitcode(BD.getProgram(), "reduced-named-md"); } // Try to clean up the testcase by running funcresolve and globaldce... if (!BugpointIsInterrupted) { outs() << "\n*** Attempting to perform final cleanups: "; Module *M = CloneModule(BD.getProgram()).release(); M = BD.performFinalCleanups(M, true).release(); // Find out if the pass still crashes on the cleaned up program... if (TestFn(BD, M)) { BD.setNewProgram(M); // Yup, it does, keep the reduced version... } else { delete M; } } BD.EmitProgressBitcode(BD.getProgram(), "reduced-simplified"); return Error::success(); }
/// DebugACrash - Given a predicate that determines whether a component crashes /// on a program, try to destructively reduce the program while still keeping /// the predicate true. static bool DebugACrash(BugDriver &BD, bool (*TestFn)(const BugDriver &, Module *), std::string &Error) { // See if we can get away with nuking some of the global variable initializers // in the program... if (!NoGlobalRM && BD.getProgram()->global_begin() != BD.getProgram()->global_end()) { // Now try to reduce the number of global variable initializers in the // module to something small. Module *M = CloneModule(BD.getProgram()); bool DeletedInit = false; for (Module::global_iterator I = M->global_begin(), E = M->global_end(); I != E; ++I) if (I->hasInitializer()) { I->setInitializer(nullptr); I->setLinkage(GlobalValue::ExternalLinkage); DeletedInit = true; } if (!DeletedInit) { delete M; // No change made... } else { // See if the program still causes a crash... outs() << "\nChecking to see if we can delete global inits: "; if (TestFn(BD, M)) { // Still crashes? BD.setNewProgram(M); outs() << "\n*** Able to remove all global initializers!\n"; } else { // No longer crashes? outs() << " - Removing all global inits hides problem!\n"; delete M; std::vector<GlobalVariable*> GVs; for (Module::global_iterator I = BD.getProgram()->global_begin(), E = BD.getProgram()->global_end(); I != E; ++I) if (I->hasInitializer()) GVs.push_back(&*I); if (GVs.size() > 1 && !BugpointIsInterrupted) { outs() << "\n*** Attempting to reduce the number of global " << "variables in the testcase\n"; unsigned OldSize = GVs.size(); ReduceCrashingGlobalVariables(BD, TestFn).reduceList(GVs, Error); if (!Error.empty()) return true; if (GVs.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-global-variables"); } } } } // Now try to reduce the number of functions in the module to something small. std::vector<Function*> Functions; for (Function &F : *BD.getProgram()) if (!F.isDeclaration()) Functions.push_back(&F); if (Functions.size() > 1 && !BugpointIsInterrupted) { outs() << "\n*** Attempting to reduce the number of functions " "in the testcase\n"; unsigned OldSize = Functions.size(); ReduceCrashingFunctions(BD, TestFn).reduceList(Functions, Error); if (Functions.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-function"); } // Attempt to delete entire basic blocks at a time to speed up // convergence... this actually works by setting the terminator of the blocks // to a return instruction then running simplifycfg, which can potentially // shrinks the code dramatically quickly // if (!DisableSimplifyCFG && !BugpointIsInterrupted) { std::vector<const BasicBlock*> Blocks; for (Function &F : *BD.getProgram()) for (BasicBlock &BB : F) Blocks.push_back(&BB); unsigned OldSize = Blocks.size(); ReduceCrashingBlocks(BD, TestFn).reduceList(Blocks, Error); if (Blocks.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-blocks"); } // Attempt to delete instructions using bisection. This should help out nasty // cases with large basic blocks where the problem is at one end. if (!BugpointIsInterrupted) { std::vector<const Instruction*> Insts; for (const Function &F : *BD.getProgram()) for (const BasicBlock &BB : F) for (const Instruction &I : BB) if (!isa<TerminatorInst>(&I)) Insts.push_back(&I); ReduceCrashingInstructions(BD, TestFn).reduceList(Insts, Error); } // FIXME: This should use the list reducer to converge faster by deleting // larger chunks of instructions at a time! unsigned Simplification = 2; do { if (BugpointIsInterrupted) break; --Simplification; outs() << "\n*** Attempting to reduce testcase by deleting instruc" << "tions: Simplification Level #" << Simplification << '\n'; // Now that we have deleted the functions that are unnecessary for the // program, try to remove instructions that are not necessary to cause the // crash. To do this, we loop through all of the instructions in the // remaining functions, deleting them (replacing any values produced with // nulls), and then running ADCE and SimplifyCFG. If the transformed input // still triggers failure, keep deleting until we cannot trigger failure // anymore. // unsigned InstructionsToSkipBeforeDeleting = 0; TryAgain: // Loop over all of the (non-terminator) instructions remaining in the // function, attempting to delete them. unsigned CurInstructionNum = 0; for (Module::const_iterator FI = BD.getProgram()->begin(), E = BD.getProgram()->end(); FI != E; ++FI) if (!FI->isDeclaration()) for (Function::const_iterator BI = FI->begin(), E = FI->end(); BI != E; ++BI) for (BasicBlock::const_iterator I = BI->begin(), E = --BI->end(); I != E; ++I, ++CurInstructionNum) { if (InstructionsToSkipBeforeDeleting) { --InstructionsToSkipBeforeDeleting; } else { if (BugpointIsInterrupted) goto ExitLoops; if (isa<LandingPadInst>(I)) continue; outs() << "Checking instruction: " << *I; std::unique_ptr<Module> M = BD.deleteInstructionFromProgram(&*I, Simplification); // Find out if the pass still crashes on this pass... if (TestFn(BD, M.get())) { // Yup, it does, we delete the old module, and continue trying // to reduce the testcase... BD.setNewProgram(M.release()); InstructionsToSkipBeforeDeleting = CurInstructionNum; goto TryAgain; // I wish I had a multi-level break here! } } } if (InstructionsToSkipBeforeDeleting) { InstructionsToSkipBeforeDeleting = 0; goto TryAgain; } } while (Simplification); ExitLoops: // Try to clean up the testcase by running funcresolve and globaldce... if (!BugpointIsInterrupted) { outs() << "\n*** Attempting to perform final cleanups: "; Module *M = CloneModule(BD.getProgram()); M = BD.performFinalCleanups(M, true).release(); // Find out if the pass still crashes on the cleaned up program... if (TestFn(BD, M)) { BD.setNewProgram(M); // Yup, it does, keep the reduced version... } else { delete M; } } BD.EmitProgressBitcode(BD.getProgram(), "reduced-simplified"); return false; }
/// CleanupAndPrepareModules - Get the specified modules ready for code /// generator testing. /// static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test, Module *Safe) { // Clean up the modules, removing extra cruft that we don't need anymore... Test = BD.performFinalCleanups(Test); // If we are executing the JIT, we have several nasty issues to take care of. if (!BD.isExecutingJIT()) return; // First, if the main function is in the Safe module, we must add a stub to // the Test module to call into it. Thus, we create a new function `main' // which just calls the old one. if (Function *oldMain = Safe->getFunction("main")) if (!oldMain->isDeclaration()) { // Rename it oldMain->setName("llvm_bugpoint_old_main"); // Create a NEW `main' function with same type in the test module. Function *newMain = Function::Create(oldMain->getFunctionType(), GlobalValue::ExternalLinkage, "main", Test); // Create an `oldmain' prototype in the test module, which will // corresponds to the real main function in the same module. Function *oldMainProto = Function::Create(oldMain->getFunctionType(), GlobalValue::ExternalLinkage, oldMain->getName(), Test); // Set up and remember the argument list for the main function. std::vector<Value*> args; for (Function::arg_iterator I = newMain->arg_begin(), E = newMain->arg_end(), OI = oldMain->arg_begin(); I != E; ++I, ++OI) { I->setName(OI->getName()); // Copy argument names from oldMain args.push_back(I); } // Call the old main function and return its result BasicBlock *BB = BasicBlock::Create(Safe->getContext(), "entry", newMain); CallInst *call = CallInst::Create(oldMainProto, args.begin(), args.end(), "", BB); // If the type of old function wasn't void, return value of call ReturnInst::Create(Safe->getContext(), call, BB); } // The second nasty issue we must deal with in the JIT is that the Safe // module cannot directly reference any functions defined in the test // module. Instead, we use a JIT API call to dynamically resolve the // symbol. // Add the resolver to the Safe module. // Prototype: void *getPointerToNamedFunction(const char* Name) Constant *resolverFunc = Safe->getOrInsertFunction("getPointerToNamedFunction", Type::getInt8PtrTy(Safe->getContext()), Type::getInt8PtrTy(Safe->getContext()), (Type *)0); // Use the function we just added to get addresses of functions we need. for (Module::iterator F = Safe->begin(), E = Safe->end(); F != E; ++F) { if (F->isDeclaration() && !F->use_empty() && &*F != resolverFunc && !F->isIntrinsic() /* ignore intrinsics */) { Function *TestFn = Test->getFunction(F->getName()); // Don't forward functions which are external in the test module too. if (TestFn && !TestFn->isDeclaration()) { // 1. Add a string constant with its name to the global file Constant *InitArray = ConstantArray::get(F->getContext(), F->getName()); GlobalVariable *funcName = new GlobalVariable(*Safe, InitArray->getType(), true /*isConstant*/, GlobalValue::InternalLinkage, InitArray, F->getName() + "_name"); // 2. Use `GetElementPtr *funcName, 0, 0' to convert the string to an // sbyte* so it matches the signature of the resolver function. // GetElementPtr *funcName, ulong 0, ulong 0 std::vector<Constant*> GEPargs(2, Constant::getNullValue(Type::getInt32Ty(F->getContext()))); Value *GEP = ConstantExpr::getGetElementPtr(funcName, &GEPargs[0], 2); std::vector<Value*> ResolverArgs; ResolverArgs.push_back(GEP); // Rewrite uses of F in global initializers, etc. to uses of a wrapper // function that dynamically resolves the calls to F via our JIT API if (!F->use_empty()) { // Create a new global to hold the cached function pointer. Constant *NullPtr = ConstantPointerNull::get(F->getType()); GlobalVariable *Cache = new GlobalVariable(*F->getParent(), F->getType(), false, GlobalValue::InternalLinkage, NullPtr,F->getName()+".fpcache"); // Construct a new stub function that will re-route calls to F const FunctionType *FuncTy = F->getFunctionType(); Function *FuncWrapper = Function::Create(FuncTy, GlobalValue::InternalLinkage, F->getName() + "_wrapper", F->getParent()); BasicBlock *EntryBB = BasicBlock::Create(F->getContext(), "entry", FuncWrapper); BasicBlock *DoCallBB = BasicBlock::Create(F->getContext(), "usecache", FuncWrapper); BasicBlock *LookupBB = BasicBlock::Create(F->getContext(), "lookupfp", FuncWrapper); // Check to see if we already looked up the value. Value *CachedVal = new LoadInst(Cache, "fpcache", EntryBB); Value *IsNull = new ICmpInst(*EntryBB, ICmpInst::ICMP_EQ, CachedVal, NullPtr, "isNull"); BranchInst::Create(LookupBB, DoCallBB, IsNull, EntryBB); // Resolve the call to function F via the JIT API: // // call resolver(GetElementPtr...) CallInst *Resolver = CallInst::Create(resolverFunc, ResolverArgs.begin(), ResolverArgs.end(), "resolver", LookupBB); // Cast the result from the resolver to correctly-typed function. CastInst *CastedResolver = new BitCastInst(Resolver, PointerType::getUnqual(F->getFunctionType()), "resolverCast", LookupBB); // Save the value in our cache. new StoreInst(CastedResolver, Cache, LookupBB); BranchInst::Create(DoCallBB, LookupBB); PHINode *FuncPtr = PHINode::Create(NullPtr->getType(), "fp", DoCallBB); FuncPtr->addIncoming(CastedResolver, LookupBB); FuncPtr->addIncoming(CachedVal, EntryBB); // Save the argument list. std::vector<Value*> Args; for (Function::arg_iterator i = FuncWrapper->arg_begin(), e = FuncWrapper->arg_end(); i != e; ++i) Args.push_back(i); // Pass on the arguments to the real function, return its result if (F->getReturnType()->isVoidTy()) { CallInst::Create(FuncPtr, Args.begin(), Args.end(), "", DoCallBB); ReturnInst::Create(F->getContext(), DoCallBB); } else { CallInst *Call = CallInst::Create(FuncPtr, Args.begin(), Args.end(), "retval", DoCallBB); ReturnInst::Create(F->getContext(),Call, DoCallBB); } // Use the wrapper function instead of the old function F->replaceAllUsesWith(FuncWrapper); } } } } if (verifyModule(*Test) || verifyModule(*Safe)) { errs() << "Bugpoint has a bug, which corrupted a module!!\n"; abort(); } }