Path: blob/main/contrib/llvm-project/llvm/tools/bugpoint/ExtractFunction.cpp
35231 views
//===- ExtractFunction.cpp - Extract a function from Program --------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements several methods that are used to extract functions,9// loops, or portions of a module from the rest of the module.10//11//===----------------------------------------------------------------------===//1213#include "BugDriver.h"14#include "llvm/IR/Constants.h"15#include "llvm/IR/DataLayout.h"16#include "llvm/IR/DerivedTypes.h"17#include "llvm/IR/LLVMContext.h"18#include "llvm/IR/LegacyPassManager.h"19#include "llvm/IR/Module.h"20#include "llvm/IR/Verifier.h"21#include "llvm/Pass.h"22#include "llvm/Support/CommandLine.h"23#include "llvm/Support/Debug.h"24#include "llvm/Support/FileUtilities.h"25#include "llvm/Support/Path.h"26#include "llvm/Support/Signals.h"27#include "llvm/Support/ToolOutputFile.h"28#include "llvm/Transforms/IPO.h"29#include "llvm/Transforms/Scalar.h"30#include "llvm/Transforms/Utils/Cloning.h"31#include "llvm/Transforms/Utils/CodeExtractor.h"32#include <set>33using namespace llvm;3435#define DEBUG_TYPE "bugpoint"3637namespace llvm {38bool DisableSimplifyCFG = false;39extern cl::opt<std::string> OutputPrefix;40} // End llvm namespace4142namespace {43cl::opt<bool> NoDCE("disable-dce",44cl::desc("Do not use the -dce pass to reduce testcases"));45cl::opt<bool, true>46NoSCFG("disable-simplifycfg", cl::location(DisableSimplifyCFG),47cl::desc("Do not use the -simplifycfg pass to reduce testcases"));4849Function *globalInitUsesExternalBA(GlobalVariable *GV) {50if (!GV->hasInitializer())51return nullptr;5253Constant *I = GV->getInitializer();5455// walk the values used by the initializer56// (and recurse into things like ConstantExpr)57std::vector<Constant *> Todo;58std::set<Constant *> Done;59Todo.push_back(I);6061while (!Todo.empty()) {62Constant *V = Todo.back();63Todo.pop_back();64Done.insert(V);6566if (BlockAddress *BA = dyn_cast<BlockAddress>(V)) {67Function *F = BA->getFunction();68if (F->isDeclaration())69return F;70}7172for (User::op_iterator i = V->op_begin(), e = V->op_end(); i != e; ++i) {73Constant *C = dyn_cast<Constant>(*i);74if (C && !isa<GlobalValue>(C) && !Done.count(C))75Todo.push_back(C);76}77}78return nullptr;79}80} // end anonymous namespace8182std::unique_ptr<Module>83BugDriver::deleteInstructionFromProgram(const Instruction *I,84unsigned Simplification) {85// FIXME, use vmap?86std::unique_ptr<Module> Clone = CloneModule(*Program);8788const BasicBlock *PBB = I->getParent();89const Function *PF = PBB->getParent();9091Module::iterator RFI = Clone->begin(); // Get iterator to corresponding fn92std::advance(93RFI, std::distance(PF->getParent()->begin(), Module::const_iterator(PF)));9495Function::iterator RBI = RFI->begin(); // Get iterator to corresponding BB96std::advance(RBI, std::distance(PF->begin(), Function::const_iterator(PBB)));9798BasicBlock::iterator RI = RBI->begin(); // Get iterator to corresponding inst99std::advance(RI, std::distance(PBB->begin(), BasicBlock::const_iterator(I)));100Instruction *TheInst = &*RI; // Got the corresponding instruction!101102// If this instruction produces a value, replace any users with null values103if (!TheInst->getType()->isVoidTy())104TheInst->replaceAllUsesWith(Constant::getNullValue(TheInst->getType()));105106// Remove the instruction from the program.107TheInst->eraseFromParent();108109// Spiff up the output a little bit.110std::vector<std::string> Passes;111112/// Can we get rid of the -disable-* options?113if (Simplification > 1 && !NoDCE)114Passes.push_back("dce");115if (Simplification && !DisableSimplifyCFG)116Passes.push_back("simplifycfg"); // Delete dead control flow117118Passes.push_back("verify");119std::unique_ptr<Module> New = runPassesOn(Clone.get(), Passes);120if (!New) {121errs() << "Instruction removal failed. Sorry. :( Please report a bug!\n";122exit(1);123}124return New;125}126127std::unique_ptr<Module>128BugDriver::performFinalCleanups(std::unique_ptr<Module> M,129bool MayModifySemantics) {130// Make all functions external, so GlobalDCE doesn't delete them...131for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)132I->setLinkage(GlobalValue::ExternalLinkage);133134std::vector<std::string> CleanupPasses;135136if (MayModifySemantics)137CleanupPasses.push_back("deadarghaX0r");138else139CleanupPasses.push_back("deadargelim");140141std::unique_ptr<Module> New = runPassesOn(M.get(), CleanupPasses);142if (!New) {143errs() << "Final cleanups failed. Sorry. :( Please report a bug!\n";144return nullptr;145}146return New;147}148149std::unique_ptr<Module> BugDriver::extractLoop(Module *M) {150std::vector<std::string> LoopExtractPasses;151LoopExtractPasses.push_back("loop-extract-single");152153std::unique_ptr<Module> NewM = runPassesOn(M, LoopExtractPasses);154if (!NewM) {155outs() << "*** Loop extraction failed: ";156EmitProgressBitcode(*M, "loopextraction", true);157outs() << "*** Sorry. :( Please report a bug!\n";158return nullptr;159}160161// Check to see if we created any new functions. If not, no loops were162// extracted and we should return null. Limit the number of loops we extract163// to avoid taking forever.164static unsigned NumExtracted = 32;165if (M->size() == NewM->size() || --NumExtracted == 0) {166return nullptr;167} else {168assert(M->size() < NewM->size() && "Loop extract removed functions?");169Module::iterator MI = NewM->begin();170for (unsigned i = 0, e = M->size(); i != e; ++i)171++MI;172}173174return NewM;175}176177static void eliminateAliases(GlobalValue *GV) {178// First, check whether a GlobalAlias references this definition.179// GlobalAlias MAY NOT reference declarations.180for (;;) {181// 1. Find aliases182SmallVector<GlobalAlias *, 1> aliases;183Module *M = GV->getParent();184for (Module::alias_iterator I = M->alias_begin(), E = M->alias_end();185I != E; ++I)186if (I->getAliasee()->stripPointerCasts() == GV)187aliases.push_back(&*I);188if (aliases.empty())189break;190// 2. Resolve aliases191for (unsigned i = 0, e = aliases.size(); i < e; ++i) {192aliases[i]->replaceAllUsesWith(aliases[i]->getAliasee());193aliases[i]->eraseFromParent();194}195// 3. Repeat until no more aliases found; there might196// be an alias to an alias...197}198}199200//201// DeleteGlobalInitializer - "Remove" the global variable by deleting its202// initializer,203// making it external.204//205void llvm::DeleteGlobalInitializer(GlobalVariable *GV) {206eliminateAliases(GV);207GV->setInitializer(nullptr);208GV->setComdat(nullptr);209}210211// DeleteFunctionBody - "Remove" the function by deleting all of its basic212// blocks, making it external.213//214void llvm::DeleteFunctionBody(Function *F) {215eliminateAliases(F);216// Function declarations can't have comdats.217F->setComdat(nullptr);218219// delete the body of the function...220F->deleteBody();221assert(F->isDeclaration() && "This didn't make the function external!");222}223224/// GetTorInit - Given a list of entries for static ctors/dtors, return them225/// as a constant array.226static Constant *GetTorInit(std::vector<std::pair<Function *, int>> &TorList) {227assert(!TorList.empty() && "Don't create empty tor list!");228std::vector<Constant *> ArrayElts;229Type *Int32Ty = Type::getInt32Ty(TorList[0].first->getContext());230231StructType *STy = StructType::get(Int32Ty, TorList[0].first->getType());232for (unsigned i = 0, e = TorList.size(); i != e; ++i) {233Constant *Elts[] = {ConstantInt::get(Int32Ty, TorList[i].second),234TorList[i].first};235ArrayElts.push_back(ConstantStruct::get(STy, Elts));236}237return ConstantArray::get(238ArrayType::get(ArrayElts[0]->getType(), ArrayElts.size()), ArrayElts);239}240241/// SplitStaticCtorDtor - A module was recently split into two parts, M1/M2, and242/// M1 has all of the global variables. If M2 contains any functions that are243/// static ctors/dtors, we need to add an llvm.global_[cd]tors global to M2, and244/// prune appropriate entries out of M1s list.245static void SplitStaticCtorDtor(const char *GlobalName, Module *M1, Module *M2,246ValueToValueMapTy &VMap) {247GlobalVariable *GV = M1->getNamedGlobal(GlobalName);248if (!GV || GV->isDeclaration() || GV->hasLocalLinkage() || !GV->use_empty())249return;250251std::vector<std::pair<Function *, int>> M1Tors, M2Tors;252ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer());253if (!InitList)254return;255256for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) {257if (ConstantStruct *CS =258dyn_cast<ConstantStruct>(InitList->getOperand(i))) {259if (CS->getNumOperands() != 2)260return; // Not array of 2-element structs.261262if (CS->getOperand(1)->isNullValue())263break; // Found a null terminator, stop here.264265ConstantInt *CI = dyn_cast<ConstantInt>(CS->getOperand(0));266int Priority = CI ? CI->getSExtValue() : 0;267268Constant *FP = CS->getOperand(1);269if (ConstantExpr *CE = dyn_cast<ConstantExpr>(FP))270if (CE->isCast())271FP = CE->getOperand(0);272if (Function *F = dyn_cast<Function>(FP)) {273if (!F->isDeclaration())274M1Tors.push_back(std::make_pair(F, Priority));275else {276// Map to M2's version of the function.277F = cast<Function>(VMap[F]);278M2Tors.push_back(std::make_pair(F, Priority));279}280}281}282}283284GV->eraseFromParent();285if (!M1Tors.empty()) {286Constant *M1Init = GetTorInit(M1Tors);287new GlobalVariable(*M1, M1Init->getType(), false,288GlobalValue::AppendingLinkage, M1Init, GlobalName);289}290291GV = M2->getNamedGlobal(GlobalName);292assert(GV && "Not a clone of M1?");293assert(GV->use_empty() && "llvm.ctors shouldn't have uses!");294295GV->eraseFromParent();296if (!M2Tors.empty()) {297Constant *M2Init = GetTorInit(M2Tors);298new GlobalVariable(*M2, M2Init->getType(), false,299GlobalValue::AppendingLinkage, M2Init, GlobalName);300}301}302303std::unique_ptr<Module>304llvm::SplitFunctionsOutOfModule(Module *M, const std::vector<Function *> &F,305ValueToValueMapTy &VMap) {306// Make sure functions & globals are all external so that linkage307// between the two modules will work.308for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)309I->setLinkage(GlobalValue::ExternalLinkage);310for (Module::global_iterator I = M->global_begin(), E = M->global_end();311I != E; ++I) {312if (I->hasName() && I->getName()[0] == '\01')313I->setName(I->getName().substr(1));314I->setLinkage(GlobalValue::ExternalLinkage);315}316317ValueToValueMapTy NewVMap;318std::unique_ptr<Module> New = CloneModule(*M, NewVMap);319320// Remove the Test functions from the Safe module321std::set<Function *> TestFunctions;322for (unsigned i = 0, e = F.size(); i != e; ++i) {323Function *TNOF = cast<Function>(VMap[F[i]]);324LLVM_DEBUG(errs() << "Removing function ");325LLVM_DEBUG(TNOF->printAsOperand(errs(), false));326LLVM_DEBUG(errs() << "\n");327TestFunctions.insert(cast<Function>(NewVMap[TNOF]));328DeleteFunctionBody(TNOF); // Function is now external in this module!329}330331// Remove the Safe functions from the Test module332for (Function &I : *New)333if (!TestFunctions.count(&I))334DeleteFunctionBody(&I);335336// Try to split the global initializers evenly337for (GlobalVariable &I : M->globals()) {338GlobalVariable *GV = cast<GlobalVariable>(NewVMap[&I]);339if (Function *TestFn = globalInitUsesExternalBA(&I)) {340if (Function *SafeFn = globalInitUsesExternalBA(GV)) {341errs() << "*** Error: when reducing functions, encountered "342"the global '";343GV->printAsOperand(errs(), false);344errs() << "' with an initializer that references blockaddresses "345"from safe function '"346<< SafeFn->getName() << "' and from test function '"347<< TestFn->getName() << "'.\n";348exit(1);349}350DeleteGlobalInitializer(&I); // Delete the initializer to make it external351} else {352// If we keep it in the safe module, then delete it in the test module353DeleteGlobalInitializer(GV);354}355}356357// Make sure that there is a global ctor/dtor array in both halves of the358// module if they both have static ctor/dtor functions.359SplitStaticCtorDtor("llvm.global_ctors", M, New.get(), NewVMap);360SplitStaticCtorDtor("llvm.global_dtors", M, New.get(), NewVMap);361362return New;363}364365//===----------------------------------------------------------------------===//366// Basic Block Extraction Code367//===----------------------------------------------------------------------===//368369std::unique_ptr<Module>370BugDriver::extractMappedBlocksFromModule(const std::vector<BasicBlock *> &BBs,371Module *M) {372auto Temp = sys::fs::TempFile::create(OutputPrefix + "-extractblocks%%%%%%%");373if (!Temp) {374outs() << "*** Basic Block extraction failed!\n";375errs() << "Error creating temporary file: " << toString(Temp.takeError())376<< "\n";377EmitProgressBitcode(*M, "basicblockextractfail", true);378return nullptr;379}380DiscardTemp Discard{*Temp};381382// Extract all of the blocks except the ones in BBs.383SmallVector<BasicBlock *, 32> BlocksToExtract;384for (Function &F : *M)385for (BasicBlock &BB : F)386// Check if this block is going to be extracted.387if (!llvm::is_contained(BBs, &BB))388BlocksToExtract.push_back(&BB);389390raw_fd_ostream OS(Temp->FD, /*shouldClose*/ false);391for (BasicBlock *BB : BBs) {392// If the BB doesn't have a name, give it one so we have something to key393// off of.394if (!BB->hasName())395BB->setName("tmpbb");396OS << BB->getParent()->getName() << " " << BB->getName() << "\n";397}398OS.flush();399if (OS.has_error()) {400errs() << "Error writing list of blocks to not extract\n";401EmitProgressBitcode(*M, "basicblockextractfail", true);402OS.clear_error();403return nullptr;404}405406std::string uniqueFN = "--extract-blocks-file=";407uniqueFN += Temp->TmpName;408409std::vector<std::string> PI;410PI.push_back("extract-blocks");411std::unique_ptr<Module> Ret = runPassesOn(M, PI, {uniqueFN});412413if (!Ret) {414outs() << "*** Basic Block extraction failed, please report a bug!\n";415EmitProgressBitcode(*M, "basicblockextractfail", true);416}417return Ret;418}419420421