X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=tools%2Fbugpoint%2FExtractFunction.cpp;h=21d3d98459966db6364e7270e05102fe781958da;hb=8de34ad8f7dd5c159c1989c011fa70fd6f62026a;hp=145942c886766fc546dcd7ff3eb225c02d7ab4d2;hpb=8fa3338ed2400c1352b137613d2c2c70d1ead695;p=oota-llvm.git diff --git a/tools/bugpoint/ExtractFunction.cpp b/tools/bugpoint/ExtractFunction.cpp index 145942c8867..21d3d984599 100644 --- a/tools/bugpoint/ExtractFunction.cpp +++ b/tools/bugpoint/ExtractFunction.cpp @@ -13,29 +13,32 @@ //===----------------------------------------------------------------------===// #include "BugDriver.h" -#include "llvm/Constants.h" -#include "llvm/DerivedTypes.h" -#include "llvm/LLVMContext.h" -#include "llvm/Module.h" -#include "llvm/PassManager.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" #include "llvm/Pass.h" -#include "llvm/Analysis/Verifier.h" -#include "llvm/Assembly/Writer.h" -#include "llvm/Transforms/IPO.h" -#include "llvm/Transforms/Scalar.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/Transforms/Utils/FunctionUtils.h" -#include "llvm/Target/TargetData.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileUtilities.h" -#include "llvm/System/Path.h" -#include "llvm/System/Signals.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/CodeExtractor.h" #include using namespace llvm; +#define DEBUG_TYPE "bugpoint" + namespace llvm { bool DisableSimplifyCFG = false; + extern cl::opt OutputPrefix; } // End llvm namespace namespace { @@ -45,21 +48,50 @@ namespace { cl::opt NoSCFG("disable-simplifycfg", cl::location(DisableSimplifyCFG), cl::desc("Do not use the -simplifycfg pass to reduce testcases")); -} -/// deleteInstructionFromProgram - This method clones the current Program and -/// deletes the specified instruction from the cloned module. It then runs a -/// series of cleanup passes (ADCE and SimplifyCFG) to eliminate any code which -/// depends on the value. The modified module is then returned. -/// -Module *BugDriver::deleteInstructionFromProgram(const Instruction *I, - unsigned Simplification) const { - Module *Result = CloneModule(Program); + Function* globalInitUsesExternalBA(GlobalVariable* GV) { + if (!GV->hasInitializer()) + return nullptr; + + Constant *I = GV->getInitializer(); + + // walk the values used by the initializer + // (and recurse into things like ConstantExpr) + std::vector Todo; + std::set Done; + Todo.push_back(I); + + while (!Todo.empty()) { + Constant* V = Todo.back(); + Todo.pop_back(); + Done.insert(V); + + if (BlockAddress *BA = dyn_cast(V)) { + Function *F = BA->getFunction(); + if (F->isDeclaration()) + return F; + } + + for (User::op_iterator i = V->op_begin(), e = V->op_end(); i != e; ++i) { + Constant *C = dyn_cast(*i); + if (C && !isa(C) && !Done.count(C)) + Todo.push_back(C); + } + } + return nullptr; + } +} // end anonymous namespace + +std::unique_ptr +BugDriver::deleteInstructionFromProgram(const Instruction *I, + unsigned Simplification) { + // FIXME, use vmap? + Module *Clone = CloneModule(Program); const BasicBlock *PBB = I->getParent(); const Function *PF = PBB->getParent(); - Module::iterator RFI = Result->begin(); // Get iterator to corresponding fn + Module::iterator RFI = Clone->begin(); // Get iterator to corresponding fn std::advance(RFI, std::distance(PF->getParent()->begin(), Module::const_iterator(PF))); @@ -68,86 +100,67 @@ Module *BugDriver::deleteInstructionFromProgram(const Instruction *I, BasicBlock::iterator RI = RBI->begin(); // Get iterator to corresponding inst std::advance(RI, std::distance(PBB->begin(), BasicBlock::const_iterator(I))); - Instruction *TheInst = RI; // Got the corresponding instruction! + Instruction *TheInst = &*RI; // Got the corresponding instruction! // If this instruction produces a value, replace any users with null values - if (isa(TheInst->getType())) - TheInst->replaceAllUsesWith(Context.getUndef(TheInst->getType())); - else if (TheInst->getType() != Type::VoidTy) - TheInst->replaceAllUsesWith(Context.getNullValue(TheInst->getType())); + if (!TheInst->getType()->isVoidTy()) + TheInst->replaceAllUsesWith(Constant::getNullValue(TheInst->getType())); // Remove the instruction from the program. TheInst->getParent()->getInstList().erase(TheInst); - - //writeProgramToFile("current.bc", Result); - // Spiff up the output a little bit. - PassManager Passes; - // Make sure that the appropriate target data is always used... - Passes.add(new TargetData(Result)); + std::vector Passes; - /// FIXME: If this used runPasses() like the methods below, we could get rid - /// of the -disable-* options! + /// Can we get rid of the -disable-* options? if (Simplification > 1 && !NoDCE) - Passes.add(createDeadCodeEliminationPass()); + Passes.push_back("dce"); if (Simplification && !DisableSimplifyCFG) - Passes.add(createCFGSimplificationPass()); // Delete dead control flow - - Passes.add(createVerifierPass()); - Passes.run(*Result); - return Result; -} - -static const PassInfo *getPI(Pass *P) { - const PassInfo *PI = P->getPassInfo(); - delete P; - return PI; + Passes.push_back("simplifycfg"); // Delete dead control flow + + Passes.push_back("verify"); + std::unique_ptr New = runPassesOn(Clone, Passes); + delete Clone; + if (!New) { + errs() << "Instruction removal failed. Sorry. :( Please report a bug!\n"; + exit(1); + } + return New; } -/// performFinalCleanups - This method clones the current Program and performs -/// a series of cleanups intended to get rid of extra cruft on the module -/// before handing it to the user. -/// -Module *BugDriver::performFinalCleanups(Module *M, bool MayModifySemantics) { +std::unique_ptr +BugDriver::performFinalCleanups(Module *M, bool MayModifySemantics) { // Make all functions external, so GlobalDCE doesn't delete them... for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) I->setLinkage(GlobalValue::ExternalLinkage); - std::vector CleanupPasses; - CleanupPasses.push_back(getPI(createGlobalDCEPass())); - CleanupPasses.push_back(getPI(createDeadTypeEliminationPass())); + std::vector CleanupPasses; + CleanupPasses.push_back("globaldce"); if (MayModifySemantics) - CleanupPasses.push_back(getPI(createDeadArgHackingPass())); + CleanupPasses.push_back("deadarghaX0r"); else - CleanupPasses.push_back(getPI(createDeadArgEliminationPass())); + CleanupPasses.push_back("deadargelim"); - Module *New = runPassesOn(M, CleanupPasses); - if (New == 0) { + std::unique_ptr New = runPassesOn(M, CleanupPasses); + if (!New) { errs() << "Final cleanups failed. Sorry. :( Please report a bug!\n"; - return M; + return nullptr; } delete M; return New; } +std::unique_ptr BugDriver::extractLoop(Module *M) { + std::vector LoopExtractPasses; + LoopExtractPasses.push_back("loop-extract-single"); -/// ExtractLoop - Given a module, extract up to one loop from it into a new -/// function. This returns null if there are no extractable loops in the -/// program or if the loop extractor crashes. -Module *BugDriver::ExtractLoop(Module *M) { - std::vector LoopExtractPasses; - LoopExtractPasses.push_back(getPI(createSingleLoopExtractorPass())); - - Module *NewM = runPassesOn(M, LoopExtractPasses); - if (NewM == 0) { - Module *Old = swapProgramIn(M); + std::unique_ptr NewM = runPassesOn(M, LoopExtractPasses); + if (!NewM) { outs() << "*** Loop extraction failed: "; - EmitProgressBitcode("loopextraction", true); + EmitProgressBitcode(M, "loopextraction", true); outs() << "*** Sorry. :( Please report a bug!\n"; - swapProgramIn(Old); - return 0; + return nullptr; } // Check to see if we created any new functions. If not, no loops were @@ -155,8 +168,7 @@ Module *BugDriver::ExtractLoop(Module *M) { // to avoid taking forever. static unsigned NumExtracted = 32; if (M->size() == NewM->size() || --NumExtracted == 0) { - delete NewM; - return 0; + return nullptr; } else { assert(M->size() < NewM->size() && "Loop extract removed functions?"); Module::iterator MI = NewM->begin(); @@ -181,15 +193,19 @@ void llvm::DeleteFunctionBody(Function *F) { /// as a constant array. static Constant *GetTorInit(std::vector > &TorList) { assert(!TorList.empty() && "Don't create empty tor list!"); - LLVMContext &Context = TorList[0].first->getContext(); std::vector ArrayElts; + Type *Int32Ty = Type::getInt32Ty(TorList[0].first->getContext()); + + StructType *STy = + StructType::get(Int32Ty, TorList[0].first->getType(), nullptr); for (unsigned i = 0, e = TorList.size(); i != e; ++i) { - std::vector Elts; - Elts.push_back(ConstantInt::get(Type::Int32Ty, TorList[i].second)); - Elts.push_back(TorList[i].first); - ArrayElts.push_back(ConstantStruct::get(Elts)); + Constant *Elts[] = { + ConstantInt::get(Int32Ty, TorList[i].second), + TorList[i].first + }; + ArrayElts.push_back(ConstantStruct::get(STy, Elts)); } - return Context.getConstantArray(Context.getArrayType(ArrayElts[0]->getType(), + return ConstantArray::get(ArrayType::get(ArrayElts[0]->getType(), ArrayElts.size()), ArrayElts); } @@ -199,7 +215,7 @@ static Constant *GetTorInit(std::vector > &TorList) { /// static ctors/dtors, we need to add an llvm.global_[cd]tors global to M2, and /// prune appropriate entries out of M1s list. static void SplitStaticCtorDtor(const char *GlobalName, Module *M1, Module *M2, - DenseMap ValueMap) { + ValueToValueMapTy &VMap) { GlobalVariable *GV = M1->getNamedGlobal(GlobalName); if (!GV || GV->isDeclaration() || GV->hasLocalLinkage() || !GV->use_empty()) return; @@ -227,7 +243,7 @@ static void SplitStaticCtorDtor(const char *GlobalName, Module *M1, Module *M2, M1Tors.push_back(std::make_pair(F, Priority)); else { // Map to M2's version of the function. - F = cast(ValueMap[F]); + F = cast(VMap[F]); M2Tors.push_back(std::make_pair(F, Priority)); } } @@ -262,7 +278,7 @@ static void SplitStaticCtorDtor(const char *GlobalName, Module *M1, Module *M2, Module * llvm::SplitFunctionsOutOfModule(Module *M, const std::vector &F, - DenseMap &ValueMap) { + ValueToValueMapTy &VMap) { // Make sure functions & globals are all external so that linkage // between the two modules will work. for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) @@ -274,36 +290,50 @@ llvm::SplitFunctionsOutOfModule(Module *M, I->setLinkage(GlobalValue::ExternalLinkage); } - DenseMap NewValueMap; - Module *New = CloneModule(M, NewValueMap); - - // Make sure global initializers exist only in the safe module (CBE->.so) - for (Module::global_iterator I = New->global_begin(), E = New->global_end(); - I != E; ++I) - I->setInitializer(0); // Delete the initializer to make it external + ValueToValueMapTy NewVMap; + Module *New = CloneModule(M, NewVMap); // Remove the Test functions from the Safe module std::set TestFunctions; for (unsigned i = 0, e = F.size(); i != e; ++i) { - Function *TNOF = cast(ValueMap[F[i]]); + Function *TNOF = cast(VMap[F[i]]); DEBUG(errs() << "Removing function "); - DEBUG(WriteAsOperand(errs(), TNOF, false)); + DEBUG(TNOF->printAsOperand(errs(), false)); DEBUG(errs() << "\n"); - TestFunctions.insert(cast(NewValueMap[TNOF])); + TestFunctions.insert(cast(NewVMap[TNOF])); DeleteFunctionBody(TNOF); // Function is now external in this module! } // Remove the Safe functions from the Test module - for (Module::iterator I = New->begin(), E = New->end(); I != E; ++I) - if (!TestFunctions.count(I)) - DeleteFunctionBody(I); - + for (Function &I : *New) + if (!TestFunctions.count(&I)) + DeleteFunctionBody(&I); + + // Try to split the global initializers evenly + for (GlobalVariable &I : M->globals()) { + GlobalVariable *GV = cast(NewVMap[&I]); + if (Function *TestFn = globalInitUsesExternalBA(&I)) { + if (Function *SafeFn = globalInitUsesExternalBA(GV)) { + errs() << "*** Error: when reducing functions, encountered " + "the global '"; + GV->printAsOperand(errs(), false); + errs() << "' with an initializer that references blockaddresses " + "from safe function '" << SafeFn->getName() + << "' and from test function '" << TestFn->getName() << "'.\n"; + exit(1); + } + I.setInitializer(nullptr); // Delete the initializer to make it external + } else { + // If we keep it in the safe module, then delete it in the test module + GV->setInitializer(nullptr); + } + } // Make sure that there is a global ctor/dtor array in both halves of the // module if they both have static ctor/dtor functions. - SplitStaticCtorDtor("llvm.global_ctors", M, New, NewValueMap); - SplitStaticCtorDtor("llvm.global_dtors", M, New, NewValueMap); + SplitStaticCtorDtor("llvm.global_ctors", M, New, NewVMap); + SplitStaticCtorDtor("llvm.global_dtors", M, New, NewVMap); return New; } @@ -312,70 +342,53 @@ llvm::SplitFunctionsOutOfModule(Module *M, // Basic Block Extraction Code //===----------------------------------------------------------------------===// -/// ExtractMappedBlocksFromModule - Extract all but the specified basic blocks -/// into their own functions. The only detail is that M is actually a module -/// cloned from the one the BBs are in, so some mapping needs to be performed. -/// If this operation fails for some reason (ie the implementation is buggy), -/// this function should return null, otherwise it returns a new Module. -Module *BugDriver::ExtractMappedBlocksFromModule(const - std::vector &BBs, - Module *M) { - char *ExtraArg = NULL; - - sys::Path uniqueFilename("bugpoint-extractblocks"); - std::string ErrMsg; - if (uniqueFilename.createTemporaryFileOnDisk(true, &ErrMsg)) { +std::unique_ptr +BugDriver::extractMappedBlocksFromModule(const std::vector &BBs, + Module *M) { + SmallString<128> Filename; + int FD; + std::error_code EC = sys::fs::createUniqueFile( + OutputPrefix + "-extractblocks%%%%%%%", FD, Filename); + if (EC) { outs() << "*** Basic Block extraction failed!\n"; - errs() << "Error creating temporary file: " << ErrMsg << "\n"; - M = swapProgramIn(M); - EmitProgressBitcode("basicblockextractfail", true); - swapProgramIn(M); - return 0; + errs() << "Error creating temporary file: " << EC.message() << "\n"; + EmitProgressBitcode(M, "basicblockextractfail", true); + return nullptr; } - sys::RemoveFileOnSignal(uniqueFilename); + sys::RemoveFileOnSignal(Filename); - std::string ErrorInfo; - raw_fd_ostream BlocksToNotExtractFile(uniqueFilename.c_str(), - /*Binary=*/false, /*Force=*/true, - ErrorInfo); - if (!ErrorInfo.empty()) { - outs() << "*** Basic Block extraction failed!\n"; - errs() << "Error writing list of blocks to not extract: " << ErrorInfo - << "\n"; - M = swapProgramIn(M); - EmitProgressBitcode("basicblockextractfail", true); - swapProgramIn(M); - return 0; - } + tool_output_file BlocksToNotExtractFile(Filename.c_str(), FD); for (std::vector::const_iterator I = BBs.begin(), E = BBs.end(); I != E; ++I) { BasicBlock *BB = *I; // If the BB doesn't have a name, give it one so we have something to key // off of. if (!BB->hasName()) BB->setName("tmpbb"); - BlocksToNotExtractFile << BB->getParent()->getNameStr() << " " - << BB->getName() << "\n"; + BlocksToNotExtractFile.os() << BB->getParent()->getName() << " " + << BB->getName() << "\n"; + } + BlocksToNotExtractFile.os().close(); + if (BlocksToNotExtractFile.os().has_error()) { + errs() << "Error writing list of blocks to not extract\n"; + EmitProgressBitcode(M, "basicblockextractfail", true); + BlocksToNotExtractFile.os().clear_error(); + return nullptr; } - BlocksToNotExtractFile.close(); + BlocksToNotExtractFile.keep(); - const char *uniqueFN = uniqueFilename.c_str(); - ExtraArg = (char*)malloc(23 + strlen(uniqueFN)); - strcat(strcpy(ExtraArg, "--extract-blocks-file="), uniqueFN); + std::string uniqueFN = "--extract-blocks-file="; + uniqueFN += Filename.str(); + const char *ExtraArg = uniqueFN.c_str(); - std::vector PI; - std::vector EmptyBBs; // This parameter is ignored. - PI.push_back(getPI(createBlockExtractorPass(EmptyBBs))); - Module *Ret = runPassesOn(M, PI, false, 1, &ExtraArg); + std::vector PI; + PI.push_back("extract-blocks"); + std::unique_ptr Ret = runPassesOn(M, PI, false, 1, &ExtraArg); - if (uniqueFilename.exists()) - uniqueFilename.eraseFromDisk(); // Free disk space - free(ExtraArg); + sys::fs::remove(Filename.c_str()); - if (Ret == 0) { + if (!Ret) { outs() << "*** Basic Block extraction failed, please report a bug!\n"; - M = swapProgramIn(M); - EmitProgressBitcode("basicblockextractfail", true); - swapProgramIn(M); + EmitProgressBitcode(M, "basicblockextractfail", true); } return Ret; }