예제 #1
0
static Error ReduceGlobalInitializers(BugDriver &BD,
                                      bool (*TestFn)(const BugDriver &,
                                                     Module *)) {
  if (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()).release();
    bool DeletedInit = false;

    for (Module::global_iterator I = M->global_begin(), E = M->global_end();
         I != E; ++I)
      if (I->hasInitializer()) {
        DeleteGlobalInitializer(&*I);
        I->setLinkage(GlobalValue::ExternalLinkage);
        I->setComdat(nullptr);
        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();
          Expected<bool> Result =
              ReduceCrashingGlobalVariables(BD, TestFn).reduceList(GVs);
          if (Error E = Result.takeError())
            return E;

          if (GVs.size() < OldSize)
            BD.EmitProgressBitcode(BD.getProgram(), "reduced-global-variables");
        }
      }
    }
  }
  return Error::success();
}
예제 #2
0
/// 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()).release();
        bool DeletedInit = false;

        for (Module::global_iterator I = M->global_begin(), E = M->global_end();
                I != E; ++I)
            if (I->hasInitializer()) {
                DeleteGlobalInitializer(&*I);
                I->setLinkage(GlobalValue::ExternalLinkage);
                I->setComdat(nullptr);
                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 (I->isEHPad() || I->getType()->isTokenTy())
                                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);

    if (!NoNamedMDRM) {
        BD.EmitProgressBitcode(BD.getProgram(), "reduced-instructions");

        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());
            ReduceCrashingNamedMD(BD, TestFn).reduceList(NamedMDNames, Error);
        }

        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);
            ReduceCrashingNamedMDOps(BD, TestFn).reduceList(NamedMDOps, Error);
        }
    }

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()).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 false;
}