1 //===- ListReducer.h - Trim down list while retaining property --*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This class is to be used as a base class for operations that want to zero in
10 // on a subset of the input which still causes the bug we are tracking.
12 //===----------------------------------------------------------------------===//
14 #ifndef LLVM_TOOLS_BUGPOINT_LISTREDUCER_H
15 #define LLVM_TOOLS_BUGPOINT_LISTREDUCER_H
17 #include "llvm/Support/Error.h"
18 #include "llvm/Support/raw_ostream.h"
26 extern bool BugpointIsInterrupted
;
28 template <typename ElTy
> struct ListReducer
{
30 NoFailure
, // No failure of the predicate was detected
31 KeepSuffix
, // The suffix alone satisfies the predicate
32 KeepPrefix
// The prefix alone satisfies the predicate
35 virtual ~ListReducer() {}
37 /// This virtual function should be overriden by subclasses to implement the
38 /// test desired. The testcase is only required to test to see if the Kept
39 /// list still satisfies the property, but if it is going to check the prefix
41 virtual Expected
<TestResult
> doTest(std::vector
<ElTy
> &Prefix
,
42 std::vector
<ElTy
> &Kept
) = 0;
44 /// This function attempts to reduce the length of the specified list while
45 /// still maintaining the "test" property. This is the core of the "work"
46 /// that bugpoint does.
47 Expected
<bool> reduceList(std::vector
<ElTy
> &TheList
) {
48 std::vector
<ElTy
> empty
;
49 std::mt19937
randomness(0x6e5ea738); // Seed the random number generator
50 Expected
<TestResult
> Result
= doTest(TheList
, empty
);
51 if (Error E
= Result
.takeError())
55 if (TheList
.size() == 1) // we are done, it's the base case and it fails
58 break; // there's definitely an error, but we need to narrow it down
62 llvm_unreachable("bugpoint ListReducer internal error: "
63 "selected empty set.");
66 return false; // there is no failure with the full set of passes/funcs!
69 // Maximal number of allowed splitting iterations,
70 // before the elements are randomly shuffled.
71 const unsigned MaxIterationsWithoutProgress
= 3;
73 // Maximal number of allowed single-element trim iterations. We add a
74 // threshold here as single-element reductions may otherwise take a
75 // very long time to complete.
76 const unsigned MaxTrimIterationsWithoutBackJump
= 3;
77 bool ShufflingEnabled
= true;
80 unsigned MidTop
= TheList
.size();
81 unsigned MaxIterations
= MaxIterationsWithoutProgress
;
82 unsigned NumOfIterationsWithoutProgress
= 0;
83 while (MidTop
> 1) { // Binary split reduction loop
84 // Halt if the user presses ctrl-c.
85 if (BugpointIsInterrupted
) {
86 errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";
90 // If the loop doesn't make satisfying progress, try shuffling.
91 // The purpose of shuffling is to avoid the heavy tails of the
92 // distribution (improving the speed of convergence).
93 if (ShufflingEnabled
&& NumOfIterationsWithoutProgress
> MaxIterations
) {
94 std::vector
<ElTy
> ShuffledList(TheList
);
95 std::shuffle(ShuffledList
.begin(), ShuffledList
.end(), randomness
);
96 errs() << "\n\n*** Testing shuffled set...\n\n";
97 // Check that random shuffle doesn't lose the bug
98 Expected
<TestResult
> Result
= doTest(ShuffledList
, empty
);
99 // TODO: Previously, this error was ignored and we treated it as if
100 // shuffling hid the bug. This should really either be consumeError if
101 // that behaviour was sensible, or we should propagate the error.
102 assert(!Result
.takeError() && "Shuffling caused internal error?");
104 if (*Result
== KeepPrefix
) {
105 // If the bug is still here, use the shuffled list.
106 TheList
.swap(ShuffledList
);
107 MidTop
= TheList
.size();
108 // Must increase the shuffling treshold to avoid the small
109 // probability of infinite looping without making progress.
111 errs() << "\n\n*** Shuffling does not hide the bug...\n\n";
113 ShufflingEnabled
= false; // Disable shuffling further on
114 errs() << "\n\n*** Shuffling hides the bug...\n\n";
116 NumOfIterationsWithoutProgress
= 0;
119 unsigned Mid
= MidTop
/ 2;
120 std::vector
<ElTy
> Prefix(TheList
.begin(), TheList
.begin() + Mid
);
121 std::vector
<ElTy
> Suffix(TheList
.begin() + Mid
, TheList
.end());
123 Expected
<TestResult
> Result
= doTest(Prefix
, Suffix
);
124 if (Error E
= Result
.takeError())
128 // The property still holds. We can just drop the prefix elements, and
129 // shorten the list to the "kept" elements.
130 TheList
.swap(Suffix
);
131 MidTop
= TheList
.size();
132 // Reset progress treshold and progress counter
133 MaxIterations
= MaxIterationsWithoutProgress
;
134 NumOfIterationsWithoutProgress
= 0;
137 // The predicate still holds, shorten the list to the prefix elements.
138 TheList
.swap(Prefix
);
139 MidTop
= TheList
.size();
140 // Reset progress treshold and progress counter
141 MaxIterations
= MaxIterationsWithoutProgress
;
142 NumOfIterationsWithoutProgress
= 0;
145 // Otherwise the property doesn't hold. Some of the elements we removed
146 // must be necessary to maintain the property.
148 NumOfIterationsWithoutProgress
++;
153 // Probability of backjumping from the trimming loop back to the binary
154 // split reduction loop.
155 const int BackjumpProbability
= 10;
157 // Okay, we trimmed as much off the top and the bottom of the list as we
158 // could. If there is more than two elements in the list, try deleting
159 // interior elements and testing that.
161 if (TheList
.size() > 2) {
163 std::vector
<ElTy
> EmptyList
;
164 unsigned TrimIterations
= 0;
165 while (Changed
) { // Trimming loop.
168 // If the binary split reduction loop made an unfortunate sequence of
169 // splits, the trimming loop might be left off with a huge number of
170 // remaining elements (large search space). Backjumping out of that
171 // search space and attempting a different split can significantly
172 // improve the convergence speed.
173 if (std::rand() % 100 < BackjumpProbability
)
176 for (unsigned i
= 1; i
< TheList
.size() - 1; ++i
) {
177 // Check interior elts
178 if (BugpointIsInterrupted
) {
179 errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";
183 std::vector
<ElTy
> TestList(TheList
);
184 TestList
.erase(TestList
.begin() + i
);
186 Expected
<TestResult
> Result
= doTest(EmptyList
, TestList
);
187 if (Error E
= Result
.takeError())
189 if (*Result
== KeepSuffix
) {
190 // We can trim down the list!
191 TheList
.swap(TestList
);
192 --i
; // Don't skip an element of the list
196 if (TrimIterations
>= MaxTrimIterationsWithoutBackJump
)
202 return true; // there are some failure and we've narrowed them down
206 } // End llvm namespace