Path: blob/main/contrib/llvm-project/llvm/tools/bugpoint/ListReducer.h
35231 views
//===- ListReducer.h - Trim down list while retaining property --*- C++ -*-===//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 class is to be used as a base class for operations that want to zero in9// on a subset of the input which still causes the bug we are tracking.10//11//===----------------------------------------------------------------------===//1213#ifndef LLVM_TOOLS_BUGPOINT_LISTREDUCER_H14#define LLVM_TOOLS_BUGPOINT_LISTREDUCER_H1516#include "llvm/Support/Error.h"17#include "llvm/Support/raw_ostream.h"18#include <algorithm>19#include <cstdlib>20#include <random>21#include <vector>2223namespace llvm {2425extern bool BugpointIsInterrupted;2627template <typename ElTy> struct ListReducer {28enum TestResult {29NoFailure, // No failure of the predicate was detected30KeepSuffix, // The suffix alone satisfies the predicate31KeepPrefix // The prefix alone satisfies the predicate32};3334virtual ~ListReducer() {}3536/// This virtual function should be overriden by subclasses to implement the37/// test desired. The testcase is only required to test to see if the Kept38/// list still satisfies the property, but if it is going to check the prefix39/// anyway, it can.40virtual Expected<TestResult> doTest(std::vector<ElTy> &Prefix,41std::vector<ElTy> &Kept) = 0;4243/// This function attempts to reduce the length of the specified list while44/// still maintaining the "test" property. This is the core of the "work"45/// that bugpoint does.46Expected<bool> reduceList(std::vector<ElTy> &TheList) {47std::vector<ElTy> empty;48std::mt19937 randomness(0x6e5ea738); // Seed the random number generator49Expected<TestResult> Result = doTest(TheList, empty);50if (Error E = Result.takeError())51return std::move(E);52switch (*Result) {53case KeepPrefix:54if (TheList.size() == 1) // we are done, it's the base case and it fails55return true;56else57break; // there's definitely an error, but we need to narrow it down5859case KeepSuffix:60// cannot be reached!61llvm_unreachable("bugpoint ListReducer internal error: "62"selected empty set.");6364case NoFailure:65return false; // there is no failure with the full set of passes/funcs!66}6768// Maximal number of allowed splitting iterations,69// before the elements are randomly shuffled.70const unsigned MaxIterationsWithoutProgress = 3;7172// Maximal number of allowed single-element trim iterations. We add a73// threshold here as single-element reductions may otherwise take a74// very long time to complete.75const unsigned MaxTrimIterationsWithoutBackJump = 3;76bool ShufflingEnabled = true;7778Backjump:79unsigned MidTop = TheList.size();80unsigned MaxIterations = MaxIterationsWithoutProgress;81unsigned NumOfIterationsWithoutProgress = 0;82while (MidTop > 1) { // Binary split reduction loop83// Halt if the user presses ctrl-c.84if (BugpointIsInterrupted) {85errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";86return true;87}8889// If the loop doesn't make satisfying progress, try shuffling.90// The purpose of shuffling is to avoid the heavy tails of the91// distribution (improving the speed of convergence).92if (ShufflingEnabled && NumOfIterationsWithoutProgress > MaxIterations) {93std::vector<ElTy> ShuffledList(TheList);94llvm::shuffle(ShuffledList.begin(), ShuffledList.end(), randomness);95errs() << "\n\n*** Testing shuffled set...\n\n";96// Check that random shuffle doesn't lose the bug97Expected<TestResult> Result = doTest(ShuffledList, empty);98// TODO: Previously, this error was ignored and we treated it as if99// shuffling hid the bug. This should really either be consumeError if100// that behaviour was sensible, or we should propagate the error.101assert(!Result.takeError() && "Shuffling caused internal error?");102103if (*Result == KeepPrefix) {104// If the bug is still here, use the shuffled list.105TheList.swap(ShuffledList);106MidTop = TheList.size();107// Must increase the shuffling treshold to avoid the small108// probability of infinite looping without making progress.109MaxIterations += 2;110errs() << "\n\n*** Shuffling does not hide the bug...\n\n";111} else {112ShufflingEnabled = false; // Disable shuffling further on113errs() << "\n\n*** Shuffling hides the bug...\n\n";114}115NumOfIterationsWithoutProgress = 0;116}117118unsigned Mid = MidTop / 2;119std::vector<ElTy> Prefix(TheList.begin(), TheList.begin() + Mid);120std::vector<ElTy> Suffix(TheList.begin() + Mid, TheList.end());121122Expected<TestResult> Result = doTest(Prefix, Suffix);123if (Error E = Result.takeError())124return std::move(E);125switch (*Result) {126case KeepSuffix:127// The property still holds. We can just drop the prefix elements, and128// shorten the list to the "kept" elements.129TheList.swap(Suffix);130MidTop = TheList.size();131// Reset progress treshold and progress counter132MaxIterations = MaxIterationsWithoutProgress;133NumOfIterationsWithoutProgress = 0;134break;135case KeepPrefix:136// The predicate still holds, shorten the list to the prefix elements.137TheList.swap(Prefix);138MidTop = TheList.size();139// Reset progress treshold and progress counter140MaxIterations = MaxIterationsWithoutProgress;141NumOfIterationsWithoutProgress = 0;142break;143case NoFailure:144// Otherwise the property doesn't hold. Some of the elements we removed145// must be necessary to maintain the property.146MidTop = Mid;147NumOfIterationsWithoutProgress++;148break;149}150}151152// Probability of backjumping from the trimming loop back to the binary153// split reduction loop.154const int BackjumpProbability = 10;155156// Okay, we trimmed as much off the top and the bottom of the list as we157// could. If there is more than two elements in the list, try deleting158// interior elements and testing that.159//160if (TheList.size() > 2) {161bool Changed = true;162std::vector<ElTy> EmptyList;163unsigned TrimIterations = 0;164while (Changed) { // Trimming loop.165Changed = false;166167// If the binary split reduction loop made an unfortunate sequence of168// splits, the trimming loop might be left off with a huge number of169// remaining elements (large search space). Backjumping out of that170// search space and attempting a different split can significantly171// improve the convergence speed.172if (std::rand() % 100 < BackjumpProbability)173goto Backjump;174175for (unsigned i = 1; i < TheList.size() - 1; ++i) {176// Check interior elts177if (BugpointIsInterrupted) {178errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";179return true;180}181182std::vector<ElTy> TestList(TheList);183TestList.erase(TestList.begin() + i);184185Expected<TestResult> Result = doTest(EmptyList, TestList);186if (Error E = Result.takeError())187return std::move(E);188if (*Result == KeepSuffix) {189// We can trim down the list!190TheList.swap(TestList);191--i; // Don't skip an element of the list192Changed = true;193}194}195if (TrimIterations >= MaxTrimIterationsWithoutBackJump)196break;197TrimIterations++;198}199}200201return true; // there are some failure and we've narrowed them down202}203};204205} // End llvm namespace206207#endif208209210