xref: /freebsd/contrib/llvm-project/llvm/tools/bugpoint/ListReducer.h (revision fe6060f10f634930ff71b7c50291ddc610da2475)
10b57cec5SDimitry Andric //===- ListReducer.h - Trim down list while retaining property --*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This class is to be used as a base class for operations that want to zero in
100b57cec5SDimitry Andric // on a subset of the input which still causes the bug we are tracking.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #ifndef LLVM_TOOLS_BUGPOINT_LISTREDUCER_H
150b57cec5SDimitry Andric #define LLVM_TOOLS_BUGPOINT_LISTREDUCER_H
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric #include "llvm/Support/Error.h"
180b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
190b57cec5SDimitry Andric #include <algorithm>
200b57cec5SDimitry Andric #include <cstdlib>
210b57cec5SDimitry Andric #include <random>
220b57cec5SDimitry Andric #include <vector>
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric namespace llvm {
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric extern bool BugpointIsInterrupted;
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric template <typename ElTy> struct ListReducer {
290b57cec5SDimitry Andric   enum TestResult {
300b57cec5SDimitry Andric     NoFailure,  // No failure of the predicate was detected
310b57cec5SDimitry Andric     KeepSuffix, // The suffix alone satisfies the predicate
320b57cec5SDimitry Andric     KeepPrefix  // The prefix alone satisfies the predicate
330b57cec5SDimitry Andric   };
340b57cec5SDimitry Andric 
~ListReducerListReducer350b57cec5SDimitry Andric   virtual ~ListReducer() {}
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric   /// This virtual function should be overriden by subclasses to implement the
380b57cec5SDimitry Andric   /// test desired.  The testcase is only required to test to see if the Kept
390b57cec5SDimitry Andric   /// list still satisfies the property, but if it is going to check the prefix
400b57cec5SDimitry Andric   /// anyway, it can.
410b57cec5SDimitry Andric   virtual Expected<TestResult> doTest(std::vector<ElTy> &Prefix,
420b57cec5SDimitry Andric                                       std::vector<ElTy> &Kept) = 0;
430b57cec5SDimitry Andric 
440b57cec5SDimitry Andric   /// This function attempts to reduce the length of the specified list while
450b57cec5SDimitry Andric   /// still maintaining the "test" property.  This is the core of the "work"
460b57cec5SDimitry Andric   /// that bugpoint does.
reduceListListReducer470b57cec5SDimitry Andric   Expected<bool> reduceList(std::vector<ElTy> &TheList) {
480b57cec5SDimitry Andric     std::vector<ElTy> empty;
490b57cec5SDimitry Andric     std::mt19937 randomness(0x6e5ea738);  // Seed the random number generator
500b57cec5SDimitry Andric     Expected<TestResult> Result = doTest(TheList, empty);
510b57cec5SDimitry Andric     if (Error E = Result.takeError())
520b57cec5SDimitry Andric       return std::move(E);
530b57cec5SDimitry Andric     switch (*Result) {
540b57cec5SDimitry Andric     case KeepPrefix:
550b57cec5SDimitry Andric       if (TheList.size() == 1) // we are done, it's the base case and it fails
560b57cec5SDimitry Andric         return true;
570b57cec5SDimitry Andric       else
580b57cec5SDimitry Andric         break; // there's definitely an error, but we need to narrow it down
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric     case KeepSuffix:
610b57cec5SDimitry Andric       // cannot be reached!
620b57cec5SDimitry Andric       llvm_unreachable("bugpoint ListReducer internal error: "
630b57cec5SDimitry Andric                        "selected empty set.");
640b57cec5SDimitry Andric 
650b57cec5SDimitry Andric     case NoFailure:
660b57cec5SDimitry Andric       return false; // there is no failure with the full set of passes/funcs!
670b57cec5SDimitry Andric     }
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric     // Maximal number of allowed splitting iterations,
700b57cec5SDimitry Andric     // before the elements are randomly shuffled.
710b57cec5SDimitry Andric     const unsigned MaxIterationsWithoutProgress = 3;
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric     // Maximal number of allowed single-element trim iterations. We add a
740b57cec5SDimitry Andric     // threshold here as single-element reductions may otherwise take a
750b57cec5SDimitry Andric     // very long time to complete.
760b57cec5SDimitry Andric     const unsigned MaxTrimIterationsWithoutBackJump = 3;
770b57cec5SDimitry Andric     bool ShufflingEnabled = true;
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric   Backjump:
800b57cec5SDimitry Andric     unsigned MidTop = TheList.size();
810b57cec5SDimitry Andric     unsigned MaxIterations = MaxIterationsWithoutProgress;
820b57cec5SDimitry Andric     unsigned NumOfIterationsWithoutProgress = 0;
830b57cec5SDimitry Andric     while (MidTop > 1) { // Binary split reduction loop
840b57cec5SDimitry Andric       // Halt if the user presses ctrl-c.
850b57cec5SDimitry Andric       if (BugpointIsInterrupted) {
860b57cec5SDimitry Andric         errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";
870b57cec5SDimitry Andric         return true;
880b57cec5SDimitry Andric       }
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric       // If the loop doesn't make satisfying progress, try shuffling.
910b57cec5SDimitry Andric       // The purpose of shuffling is to avoid the heavy tails of the
920b57cec5SDimitry Andric       // distribution (improving the speed of convergence).
930b57cec5SDimitry Andric       if (ShufflingEnabled && NumOfIterationsWithoutProgress > MaxIterations) {
940b57cec5SDimitry Andric         std::vector<ElTy> ShuffledList(TheList);
95*fe6060f1SDimitry Andric         llvm::shuffle(ShuffledList.begin(), ShuffledList.end(), randomness);
960b57cec5SDimitry Andric         errs() << "\n\n*** Testing shuffled set...\n\n";
970b57cec5SDimitry Andric         // Check that random shuffle doesn't lose the bug
980b57cec5SDimitry Andric         Expected<TestResult> Result = doTest(ShuffledList, empty);
990b57cec5SDimitry Andric         // TODO: Previously, this error was ignored and we treated it as if
1000b57cec5SDimitry Andric         // shuffling hid the bug. This should really either be consumeError if
1010b57cec5SDimitry Andric         // that behaviour was sensible, or we should propagate the error.
1020b57cec5SDimitry Andric         assert(!Result.takeError() && "Shuffling caused internal error?");
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric         if (*Result == KeepPrefix) {
1050b57cec5SDimitry Andric           // If the bug is still here, use the shuffled list.
1060b57cec5SDimitry Andric           TheList.swap(ShuffledList);
1070b57cec5SDimitry Andric           MidTop = TheList.size();
1080b57cec5SDimitry Andric           // Must increase the shuffling treshold to avoid the small
1090b57cec5SDimitry Andric           // probability of infinite looping without making progress.
1100b57cec5SDimitry Andric           MaxIterations += 2;
1110b57cec5SDimitry Andric           errs() << "\n\n*** Shuffling does not hide the bug...\n\n";
1120b57cec5SDimitry Andric         } else {
1130b57cec5SDimitry Andric           ShufflingEnabled = false; // Disable shuffling further on
1140b57cec5SDimitry Andric           errs() << "\n\n*** Shuffling hides the bug...\n\n";
1150b57cec5SDimitry Andric         }
1160b57cec5SDimitry Andric         NumOfIterationsWithoutProgress = 0;
1170b57cec5SDimitry Andric       }
1180b57cec5SDimitry Andric 
1190b57cec5SDimitry Andric       unsigned Mid = MidTop / 2;
1200b57cec5SDimitry Andric       std::vector<ElTy> Prefix(TheList.begin(), TheList.begin() + Mid);
1210b57cec5SDimitry Andric       std::vector<ElTy> Suffix(TheList.begin() + Mid, TheList.end());
1220b57cec5SDimitry Andric 
1230b57cec5SDimitry Andric       Expected<TestResult> Result = doTest(Prefix, Suffix);
1240b57cec5SDimitry Andric       if (Error E = Result.takeError())
1250b57cec5SDimitry Andric         return std::move(E);
1260b57cec5SDimitry Andric       switch (*Result) {
1270b57cec5SDimitry Andric       case KeepSuffix:
1280b57cec5SDimitry Andric         // The property still holds.  We can just drop the prefix elements, and
1290b57cec5SDimitry Andric         // shorten the list to the "kept" elements.
1300b57cec5SDimitry Andric         TheList.swap(Suffix);
1310b57cec5SDimitry Andric         MidTop = TheList.size();
1320b57cec5SDimitry Andric         // Reset progress treshold and progress counter
1330b57cec5SDimitry Andric         MaxIterations = MaxIterationsWithoutProgress;
1340b57cec5SDimitry Andric         NumOfIterationsWithoutProgress = 0;
1350b57cec5SDimitry Andric         break;
1360b57cec5SDimitry Andric       case KeepPrefix:
1370b57cec5SDimitry Andric         // The predicate still holds, shorten the list to the prefix elements.
1380b57cec5SDimitry Andric         TheList.swap(Prefix);
1390b57cec5SDimitry Andric         MidTop = TheList.size();
1400b57cec5SDimitry Andric         // Reset progress treshold and progress counter
1410b57cec5SDimitry Andric         MaxIterations = MaxIterationsWithoutProgress;
1420b57cec5SDimitry Andric         NumOfIterationsWithoutProgress = 0;
1430b57cec5SDimitry Andric         break;
1440b57cec5SDimitry Andric       case NoFailure:
1450b57cec5SDimitry Andric         // Otherwise the property doesn't hold.  Some of the elements we removed
1460b57cec5SDimitry Andric         // must be necessary to maintain the property.
1470b57cec5SDimitry Andric         MidTop = Mid;
1480b57cec5SDimitry Andric         NumOfIterationsWithoutProgress++;
1490b57cec5SDimitry Andric         break;
1500b57cec5SDimitry Andric       }
1510b57cec5SDimitry Andric     }
1520b57cec5SDimitry Andric 
1530b57cec5SDimitry Andric     // Probability of backjumping from the trimming loop back to the binary
1540b57cec5SDimitry Andric     // split reduction loop.
1550b57cec5SDimitry Andric     const int BackjumpProbability = 10;
1560b57cec5SDimitry Andric 
1570b57cec5SDimitry Andric     // Okay, we trimmed as much off the top and the bottom of the list as we
1580b57cec5SDimitry Andric     // could.  If there is more than two elements in the list, try deleting
1590b57cec5SDimitry Andric     // interior elements and testing that.
1600b57cec5SDimitry Andric     //
1610b57cec5SDimitry Andric     if (TheList.size() > 2) {
1620b57cec5SDimitry Andric       bool Changed = true;
1630b57cec5SDimitry Andric       std::vector<ElTy> EmptyList;
1640b57cec5SDimitry Andric       unsigned TrimIterations = 0;
1650b57cec5SDimitry Andric       while (Changed) { // Trimming loop.
1660b57cec5SDimitry Andric         Changed = false;
1670b57cec5SDimitry Andric 
1680b57cec5SDimitry Andric         // If the binary split reduction loop made an unfortunate sequence of
1690b57cec5SDimitry Andric         // splits, the trimming loop might be left off with a huge number of
1700b57cec5SDimitry Andric         // remaining elements (large search space). Backjumping out of that
1710b57cec5SDimitry Andric         // search space and attempting a different split can significantly
1720b57cec5SDimitry Andric         // improve the convergence speed.
1730b57cec5SDimitry Andric         if (std::rand() % 100 < BackjumpProbability)
1740b57cec5SDimitry Andric           goto Backjump;
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric         for (unsigned i = 1; i < TheList.size() - 1; ++i) {
1770b57cec5SDimitry Andric           // Check interior elts
1780b57cec5SDimitry Andric           if (BugpointIsInterrupted) {
1790b57cec5SDimitry Andric             errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";
1800b57cec5SDimitry Andric             return true;
1810b57cec5SDimitry Andric           }
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric           std::vector<ElTy> TestList(TheList);
1840b57cec5SDimitry Andric           TestList.erase(TestList.begin() + i);
1850b57cec5SDimitry Andric 
1860b57cec5SDimitry Andric           Expected<TestResult> Result = doTest(EmptyList, TestList);
1870b57cec5SDimitry Andric           if (Error E = Result.takeError())
1880b57cec5SDimitry Andric             return std::move(E);
1890b57cec5SDimitry Andric           if (*Result == KeepSuffix) {
1900b57cec5SDimitry Andric             // We can trim down the list!
1910b57cec5SDimitry Andric             TheList.swap(TestList);
1920b57cec5SDimitry Andric             --i; // Don't skip an element of the list
1930b57cec5SDimitry Andric             Changed = true;
1940b57cec5SDimitry Andric           }
1950b57cec5SDimitry Andric         }
1960b57cec5SDimitry Andric         if (TrimIterations >= MaxTrimIterationsWithoutBackJump)
1970b57cec5SDimitry Andric           break;
1980b57cec5SDimitry Andric         TrimIterations++;
1990b57cec5SDimitry Andric       }
2000b57cec5SDimitry Andric     }
2010b57cec5SDimitry Andric 
2020b57cec5SDimitry Andric     return true; // there are some failure and we've narrowed them down
2030b57cec5SDimitry Andric   }
2040b57cec5SDimitry Andric };
2050b57cec5SDimitry Andric 
2060b57cec5SDimitry Andric } // End llvm namespace
2070b57cec5SDimitry Andric 
2080b57cec5SDimitry Andric #endif
209