xref: /aosp_15_r20/external/llvm/tools/bugpoint/ListReducer.h (revision 9880d6810fe72a1726cb53787c6711e909410d58)
1*9880d681SAndroid Build Coastguard Worker //===- ListReducer.h - Trim down list while retaining property --*- C++ -*-===//
2*9880d681SAndroid Build Coastguard Worker //
3*9880d681SAndroid Build Coastguard Worker //                     The LLVM Compiler Infrastructure
4*9880d681SAndroid Build Coastguard Worker //
5*9880d681SAndroid Build Coastguard Worker // This file is distributed under the University of Illinois Open Source
6*9880d681SAndroid Build Coastguard Worker // License. See LICENSE.TXT for details.
7*9880d681SAndroid Build Coastguard Worker //
8*9880d681SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
9*9880d681SAndroid Build Coastguard Worker //
10*9880d681SAndroid Build Coastguard Worker // This class is to be used as a base class for operations that want to zero in
11*9880d681SAndroid Build Coastguard Worker // on a subset of the input which still causes the bug we are tracking.
12*9880d681SAndroid Build Coastguard Worker //
13*9880d681SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
14*9880d681SAndroid Build Coastguard Worker 
15*9880d681SAndroid Build Coastguard Worker #ifndef LLVM_TOOLS_BUGPOINT_LISTREDUCER_H
16*9880d681SAndroid Build Coastguard Worker #define LLVM_TOOLS_BUGPOINT_LISTREDUCER_H
17*9880d681SAndroid Build Coastguard Worker 
18*9880d681SAndroid Build Coastguard Worker #include "llvm/Support/ErrorHandling.h"
19*9880d681SAndroid Build Coastguard Worker #include "llvm/Support/raw_ostream.h"
20*9880d681SAndroid Build Coastguard Worker #include <algorithm>
21*9880d681SAndroid Build Coastguard Worker #include <cstdlib>
22*9880d681SAndroid Build Coastguard Worker #include <vector>
23*9880d681SAndroid Build Coastguard Worker 
24*9880d681SAndroid Build Coastguard Worker namespace llvm {
25*9880d681SAndroid Build Coastguard Worker 
26*9880d681SAndroid Build Coastguard Worker   extern bool BugpointIsInterrupted;
27*9880d681SAndroid Build Coastguard Worker 
28*9880d681SAndroid Build Coastguard Worker template<typename ElTy>
29*9880d681SAndroid Build Coastguard Worker struct ListReducer {
30*9880d681SAndroid Build Coastguard Worker   enum TestResult {
31*9880d681SAndroid Build Coastguard Worker     NoFailure,         // No failure of the predicate was detected
32*9880d681SAndroid Build Coastguard Worker     KeepSuffix,        // The suffix alone satisfies the predicate
33*9880d681SAndroid Build Coastguard Worker     KeepPrefix,        // The prefix alone satisfies the predicate
34*9880d681SAndroid Build Coastguard Worker     InternalError      // Encountered an error trying to run the predicate
35*9880d681SAndroid Build Coastguard Worker   };
36*9880d681SAndroid Build Coastguard Worker 
~ListReducerListReducer37*9880d681SAndroid Build Coastguard Worker   virtual ~ListReducer() {}
38*9880d681SAndroid Build Coastguard Worker 
39*9880d681SAndroid Build Coastguard Worker   // doTest - This virtual function should be overriden by subclasses to
40*9880d681SAndroid Build Coastguard Worker   // implement the test desired.  The testcase is only required to test to see
41*9880d681SAndroid Build Coastguard Worker   // if the Kept list still satisfies the property, but if it is going to check
42*9880d681SAndroid Build Coastguard Worker   // the prefix anyway, it can.
43*9880d681SAndroid Build Coastguard Worker   //
44*9880d681SAndroid Build Coastguard Worker   virtual TestResult doTest(std::vector<ElTy> &Prefix,
45*9880d681SAndroid Build Coastguard Worker                             std::vector<ElTy> &Kept,
46*9880d681SAndroid Build Coastguard Worker                             std::string &Error) = 0;
47*9880d681SAndroid Build Coastguard Worker 
48*9880d681SAndroid Build Coastguard Worker   // reduceList - This function attempts to reduce the length of the specified
49*9880d681SAndroid Build Coastguard Worker   // list while still maintaining the "test" property.  This is the core of the
50*9880d681SAndroid Build Coastguard Worker   // "work" that bugpoint does.
51*9880d681SAndroid Build Coastguard Worker   //
reduceListListReducer52*9880d681SAndroid Build Coastguard Worker   bool reduceList(std::vector<ElTy> &TheList, std::string &Error) {
53*9880d681SAndroid Build Coastguard Worker     std::vector<ElTy> empty;
54*9880d681SAndroid Build Coastguard Worker     std::srand(0x6e5ea738); // Seed the random number generator
55*9880d681SAndroid Build Coastguard Worker     switch (doTest(TheList, empty, Error)) {
56*9880d681SAndroid Build Coastguard Worker     case KeepPrefix:
57*9880d681SAndroid Build Coastguard Worker       if (TheList.size() == 1) // we are done, it's the base case and it fails
58*9880d681SAndroid Build Coastguard Worker         return true;
59*9880d681SAndroid Build Coastguard Worker       else
60*9880d681SAndroid Build Coastguard Worker         break; // there's definitely an error, but we need to narrow it down
61*9880d681SAndroid Build Coastguard Worker 
62*9880d681SAndroid Build Coastguard Worker     case KeepSuffix:
63*9880d681SAndroid Build Coastguard Worker       // cannot be reached!
64*9880d681SAndroid Build Coastguard Worker       llvm_unreachable("bugpoint ListReducer internal error: "
65*9880d681SAndroid Build Coastguard Worker                        "selected empty set.");
66*9880d681SAndroid Build Coastguard Worker 
67*9880d681SAndroid Build Coastguard Worker     case NoFailure:
68*9880d681SAndroid Build Coastguard Worker       return false; // there is no failure with the full set of passes/funcs!
69*9880d681SAndroid Build Coastguard Worker 
70*9880d681SAndroid Build Coastguard Worker     case InternalError:
71*9880d681SAndroid Build Coastguard Worker       assert(!Error.empty());
72*9880d681SAndroid Build Coastguard Worker       return true;
73*9880d681SAndroid Build Coastguard Worker     }
74*9880d681SAndroid Build Coastguard Worker 
75*9880d681SAndroid Build Coastguard Worker     // Maximal number of allowed splitting iterations,
76*9880d681SAndroid Build Coastguard Worker     // before the elements are randomly shuffled.
77*9880d681SAndroid Build Coastguard Worker     const unsigned MaxIterationsWithoutProgress = 3;
78*9880d681SAndroid Build Coastguard Worker 
79*9880d681SAndroid Build Coastguard Worker     // Maximal number of allowed single-element trim iterations. We add a
80*9880d681SAndroid Build Coastguard Worker     // threshhold here as single-element reductions may otherwise take a
81*9880d681SAndroid Build Coastguard Worker     // very long time to complete.
82*9880d681SAndroid Build Coastguard Worker     const unsigned MaxTrimIterationsWithoutBackJump = 3;
83*9880d681SAndroid Build Coastguard Worker     bool ShufflingEnabled = true;
84*9880d681SAndroid Build Coastguard Worker 
85*9880d681SAndroid Build Coastguard Worker Backjump:
86*9880d681SAndroid Build Coastguard Worker     unsigned MidTop = TheList.size();
87*9880d681SAndroid Build Coastguard Worker     unsigned MaxIterations = MaxIterationsWithoutProgress;
88*9880d681SAndroid Build Coastguard Worker     unsigned NumOfIterationsWithoutProgress = 0;
89*9880d681SAndroid Build Coastguard Worker     while (MidTop > 1) { // Binary split reduction loop
90*9880d681SAndroid Build Coastguard Worker       // Halt if the user presses ctrl-c.
91*9880d681SAndroid Build Coastguard Worker       if (BugpointIsInterrupted) {
92*9880d681SAndroid Build Coastguard Worker         errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";
93*9880d681SAndroid Build Coastguard Worker         return true;
94*9880d681SAndroid Build Coastguard Worker       }
95*9880d681SAndroid Build Coastguard Worker 
96*9880d681SAndroid Build Coastguard Worker       // If the loop doesn't make satisfying progress, try shuffling.
97*9880d681SAndroid Build Coastguard Worker       // The purpose of shuffling is to avoid the heavy tails of the
98*9880d681SAndroid Build Coastguard Worker       // distribution (improving the speed of convergence).
99*9880d681SAndroid Build Coastguard Worker       if (ShufflingEnabled &&
100*9880d681SAndroid Build Coastguard Worker           NumOfIterationsWithoutProgress > MaxIterations) {
101*9880d681SAndroid Build Coastguard Worker         std::vector<ElTy> ShuffledList(TheList);
102*9880d681SAndroid Build Coastguard Worker         std::random_shuffle(ShuffledList.begin(), ShuffledList.end());
103*9880d681SAndroid Build Coastguard Worker         errs() << "\n\n*** Testing shuffled set...\n\n";
104*9880d681SAndroid Build Coastguard Worker         // Check that random shuffle doesn't loose the bug
105*9880d681SAndroid Build Coastguard Worker         if (doTest(ShuffledList, empty, Error) == KeepPrefix) {
106*9880d681SAndroid Build Coastguard Worker           // If the bug is still here, use the shuffled list.
107*9880d681SAndroid Build Coastguard Worker           TheList.swap(ShuffledList);
108*9880d681SAndroid Build Coastguard Worker           MidTop = TheList.size();
109*9880d681SAndroid Build Coastguard Worker           // Must increase the shuffling treshold to avoid the small
110*9880d681SAndroid Build Coastguard Worker           // probability of inifinite looping without making progress.
111*9880d681SAndroid Build Coastguard Worker           MaxIterations += 2;
112*9880d681SAndroid Build Coastguard Worker           errs() << "\n\n*** Shuffling does not hide the bug...\n\n";
113*9880d681SAndroid Build Coastguard Worker         } else {
114*9880d681SAndroid Build Coastguard Worker           ShufflingEnabled = false; // Disable shuffling further on
115*9880d681SAndroid Build Coastguard Worker           errs() << "\n\n*** Shuffling hides the bug...\n\n";
116*9880d681SAndroid Build Coastguard Worker         }
117*9880d681SAndroid Build Coastguard Worker         NumOfIterationsWithoutProgress = 0;
118*9880d681SAndroid Build Coastguard Worker       }
119*9880d681SAndroid Build Coastguard Worker 
120*9880d681SAndroid Build Coastguard Worker       unsigned Mid = MidTop / 2;
121*9880d681SAndroid Build Coastguard Worker       std::vector<ElTy> Prefix(TheList.begin(), TheList.begin()+Mid);
122*9880d681SAndroid Build Coastguard Worker       std::vector<ElTy> Suffix(TheList.begin()+Mid, TheList.end());
123*9880d681SAndroid Build Coastguard Worker 
124*9880d681SAndroid Build Coastguard Worker       switch (doTest(Prefix, Suffix, Error)) {
125*9880d681SAndroid Build Coastguard Worker       case KeepSuffix:
126*9880d681SAndroid Build Coastguard Worker         // The property still holds.  We can just drop the prefix elements, and
127*9880d681SAndroid Build Coastguard Worker         // shorten the list to the "kept" elements.
128*9880d681SAndroid Build Coastguard Worker         TheList.swap(Suffix);
129*9880d681SAndroid Build Coastguard Worker         MidTop = TheList.size();
130*9880d681SAndroid Build Coastguard Worker         // Reset progress treshold and progress counter
131*9880d681SAndroid Build Coastguard Worker         MaxIterations = MaxIterationsWithoutProgress;
132*9880d681SAndroid Build Coastguard Worker         NumOfIterationsWithoutProgress = 0;
133*9880d681SAndroid Build Coastguard Worker         break;
134*9880d681SAndroid Build Coastguard Worker       case KeepPrefix:
135*9880d681SAndroid Build Coastguard Worker         // The predicate still holds, shorten the list to the prefix elements.
136*9880d681SAndroid Build Coastguard Worker         TheList.swap(Prefix);
137*9880d681SAndroid Build Coastguard Worker         MidTop = TheList.size();
138*9880d681SAndroid Build Coastguard Worker         // Reset progress treshold and progress counter
139*9880d681SAndroid Build Coastguard Worker         MaxIterations = MaxIterationsWithoutProgress;
140*9880d681SAndroid Build Coastguard Worker         NumOfIterationsWithoutProgress = 0;
141*9880d681SAndroid Build Coastguard Worker         break;
142*9880d681SAndroid Build Coastguard Worker       case NoFailure:
143*9880d681SAndroid Build Coastguard Worker         // Otherwise the property doesn't hold.  Some of the elements we removed
144*9880d681SAndroid Build Coastguard Worker         // must be necessary to maintain the property.
145*9880d681SAndroid Build Coastguard Worker         MidTop = Mid;
146*9880d681SAndroid Build Coastguard Worker         NumOfIterationsWithoutProgress++;
147*9880d681SAndroid Build Coastguard Worker         break;
148*9880d681SAndroid Build Coastguard Worker       case InternalError:
149*9880d681SAndroid Build Coastguard Worker         return true;  // Error was set by doTest.
150*9880d681SAndroid Build Coastguard Worker       }
151*9880d681SAndroid Build Coastguard Worker       assert(Error.empty() && "doTest did not return InternalError for error");
152*9880d681SAndroid Build Coastguard Worker     }
153*9880d681SAndroid Build Coastguard Worker 
154*9880d681SAndroid Build Coastguard Worker     // Probability of backjumping from the trimming loop back to the binary
155*9880d681SAndroid Build Coastguard Worker     // split reduction loop.
156*9880d681SAndroid Build Coastguard Worker     const int BackjumpProbability = 10;
157*9880d681SAndroid Build Coastguard Worker 
158*9880d681SAndroid Build Coastguard Worker     // Okay, we trimmed as much off the top and the bottom of the list as we
159*9880d681SAndroid Build Coastguard Worker     // could.  If there is more than two elements in the list, try deleting
160*9880d681SAndroid Build Coastguard Worker     // interior elements and testing that.
161*9880d681SAndroid Build Coastguard Worker     //
162*9880d681SAndroid Build Coastguard Worker     if (TheList.size() > 2) {
163*9880d681SAndroid Build Coastguard Worker       bool Changed = true;
164*9880d681SAndroid Build Coastguard Worker       std::vector<ElTy> EmptyList;
165*9880d681SAndroid Build Coastguard Worker       unsigned TrimIterations = 0;
166*9880d681SAndroid Build Coastguard Worker       while (Changed) {  // Trimming loop.
167*9880d681SAndroid Build Coastguard Worker         Changed = false;
168*9880d681SAndroid Build Coastguard Worker 
169*9880d681SAndroid Build Coastguard Worker         // If the binary split reduction loop made an unfortunate sequence of
170*9880d681SAndroid Build Coastguard Worker         // splits, the trimming loop might be left off with a huge number of
171*9880d681SAndroid Build Coastguard Worker         // remaining elements (large search space). Backjumping out of that
172*9880d681SAndroid Build Coastguard Worker         // search space and attempting a different split can significantly
173*9880d681SAndroid Build Coastguard Worker         // improve the convergence speed.
174*9880d681SAndroid Build Coastguard Worker         if (std::rand() % 100 < BackjumpProbability)
175*9880d681SAndroid Build Coastguard Worker           goto Backjump;
176*9880d681SAndroid Build Coastguard Worker 
177*9880d681SAndroid Build Coastguard Worker         for (unsigned i = 1; i < TheList.size()-1; ++i) { // Check interior elts
178*9880d681SAndroid Build Coastguard Worker           if (BugpointIsInterrupted) {
179*9880d681SAndroid Build Coastguard Worker             errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";
180*9880d681SAndroid Build Coastguard Worker             return true;
181*9880d681SAndroid Build Coastguard Worker           }
182*9880d681SAndroid Build Coastguard Worker 
183*9880d681SAndroid Build Coastguard Worker           std::vector<ElTy> TestList(TheList);
184*9880d681SAndroid Build Coastguard Worker           TestList.erase(TestList.begin()+i);
185*9880d681SAndroid Build Coastguard Worker 
186*9880d681SAndroid Build Coastguard Worker           if (doTest(EmptyList, TestList, Error) == KeepSuffix) {
187*9880d681SAndroid Build Coastguard Worker             // We can trim down the list!
188*9880d681SAndroid Build Coastguard Worker             TheList.swap(TestList);
189*9880d681SAndroid Build Coastguard Worker             --i;  // Don't skip an element of the list
190*9880d681SAndroid Build Coastguard Worker             Changed = true;
191*9880d681SAndroid Build Coastguard Worker           }
192*9880d681SAndroid Build Coastguard Worker           if (!Error.empty())
193*9880d681SAndroid Build Coastguard Worker             return true;
194*9880d681SAndroid Build Coastguard Worker         }
195*9880d681SAndroid Build Coastguard Worker         if (TrimIterations >= MaxTrimIterationsWithoutBackJump)
196*9880d681SAndroid Build Coastguard Worker           break;
197*9880d681SAndroid Build Coastguard Worker         TrimIterations++;
198*9880d681SAndroid Build Coastguard Worker       }
199*9880d681SAndroid Build Coastguard Worker     }
200*9880d681SAndroid Build Coastguard Worker 
201*9880d681SAndroid Build Coastguard Worker     return true; // there are some failure and we've narrowed them down
202*9880d681SAndroid Build Coastguard Worker   }
203*9880d681SAndroid Build Coastguard Worker };
204*9880d681SAndroid Build Coastguard Worker 
205*9880d681SAndroid Build Coastguard Worker } // End llvm namespace
206*9880d681SAndroid Build Coastguard Worker 
207*9880d681SAndroid Build Coastguard Worker #endif
208