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