ListReducer.h 7.9 KB

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