forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			209 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- ListReducer.h - Trim down list while retaining property --*- C++ -*-===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // This class is to be used as a base class for operations that want to zero in
 | |
| // on a subset of the input which still causes the bug we are tracking.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #ifndef LLVM_TOOLS_BUGPOINT_LISTREDUCER_H
 | |
| #define LLVM_TOOLS_BUGPOINT_LISTREDUCER_H
 | |
| 
 | |
| #include "llvm/Support/Error.h"
 | |
| #include "llvm/Support/raw_ostream.h"
 | |
| #include <algorithm>
 | |
| #include <cstdlib>
 | |
| #include <random>
 | |
| #include <vector>
 | |
| 
 | |
| namespace llvm {
 | |
| 
 | |
| extern bool BugpointIsInterrupted;
 | |
| 
 | |
| template <typename ElTy> struct ListReducer {
 | |
|   enum TestResult {
 | |
|     NoFailure,  // No failure of the predicate was detected
 | |
|     KeepSuffix, // The suffix alone satisfies the predicate
 | |
|     KeepPrefix  // The prefix alone satisfies the predicate
 | |
|   };
 | |
| 
 | |
|   virtual ~ListReducer() {}
 | |
| 
 | |
|   /// This virtual function should be overriden by subclasses to implement the
 | |
|   /// test desired.  The testcase is only required to test to see if the Kept
 | |
|   /// list still satisfies the property, but if it is going to check the prefix
 | |
|   /// anyway, it can.
 | |
|   virtual Expected<TestResult> doTest(std::vector<ElTy> &Prefix,
 | |
|                                       std::vector<ElTy> &Kept) = 0;
 | |
| 
 | |
|   /// This function attempts to reduce the length of the specified list while
 | |
|   /// still maintaining the "test" property.  This is the core of the "work"
 | |
|   /// that bugpoint does.
 | |
|   Expected<bool> reduceList(std::vector<ElTy> &TheList) {
 | |
|     std::vector<ElTy> empty;
 | |
|     std::mt19937 randomness(0x6e5ea738);  // Seed the random number generator
 | |
|     Expected<TestResult> Result = doTest(TheList, empty);
 | |
|     if (Error E = Result.takeError())
 | |
|       return std::move(E);
 | |
|     switch (*Result) {
 | |
|     case KeepPrefix:
 | |
|       if (TheList.size() == 1) // we are done, it's the base case and it fails
 | |
|         return true;
 | |
|       else
 | |
|         break; // there's definitely an error, but we need to narrow it down
 | |
| 
 | |
|     case KeepSuffix:
 | |
|       // cannot be reached!
 | |
|       llvm_unreachable("bugpoint ListReducer internal error: "
 | |
|                        "selected empty set.");
 | |
| 
 | |
|     case NoFailure:
 | |
|       return false; // there is no failure with the full set of passes/funcs!
 | |
|     }
 | |
| 
 | |
|     // Maximal number of allowed splitting iterations,
 | |
|     // before the elements are randomly shuffled.
 | |
|     const unsigned MaxIterationsWithoutProgress = 3;
 | |
| 
 | |
|     // Maximal number of allowed single-element trim iterations. We add a
 | |
|     // threshold here as single-element reductions may otherwise take a
 | |
|     // very long time to complete.
 | |
|     const unsigned MaxTrimIterationsWithoutBackJump = 3;
 | |
|     bool ShufflingEnabled = true;
 | |
| 
 | |
|   Backjump:
 | |
|     unsigned MidTop = TheList.size();
 | |
|     unsigned MaxIterations = MaxIterationsWithoutProgress;
 | |
|     unsigned NumOfIterationsWithoutProgress = 0;
 | |
|     while (MidTop > 1) { // Binary split reduction loop
 | |
|       // Halt if the user presses ctrl-c.
 | |
|       if (BugpointIsInterrupted) {
 | |
|         errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";
 | |
|         return true;
 | |
|       }
 | |
| 
 | |
|       // If the loop doesn't make satisfying progress, try shuffling.
 | |
|       // The purpose of shuffling is to avoid the heavy tails of the
 | |
|       // distribution (improving the speed of convergence).
 | |
|       if (ShufflingEnabled && NumOfIterationsWithoutProgress > MaxIterations) {
 | |
|         std::vector<ElTy> ShuffledList(TheList);
 | |
|         std::shuffle(ShuffledList.begin(), ShuffledList.end(), randomness);
 | |
|         errs() << "\n\n*** Testing shuffled set...\n\n";
 | |
|         // Check that random shuffle doesn't lose the bug
 | |
|         Expected<TestResult> Result = doTest(ShuffledList, empty);
 | |
|         // TODO: Previously, this error was ignored and we treated it as if
 | |
|         // shuffling hid the bug. This should really either be consumeError if
 | |
|         // that behaviour was sensible, or we should propagate the error.
 | |
|         assert(!Result.takeError() && "Shuffling caused internal error?");
 | |
| 
 | |
|         if (*Result == KeepPrefix) {
 | |
|           // If the bug is still here, use the shuffled list.
 | |
|           TheList.swap(ShuffledList);
 | |
|           MidTop = TheList.size();
 | |
|           // Must increase the shuffling treshold to avoid the small
 | |
|           // probability of infinite looping without making progress.
 | |
|           MaxIterations += 2;
 | |
|           errs() << "\n\n*** Shuffling does not hide the bug...\n\n";
 | |
|         } else {
 | |
|           ShufflingEnabled = false; // Disable shuffling further on
 | |
|           errs() << "\n\n*** Shuffling hides the bug...\n\n";
 | |
|         }
 | |
|         NumOfIterationsWithoutProgress = 0;
 | |
|       }
 | |
| 
 | |
|       unsigned Mid = MidTop / 2;
 | |
|       std::vector<ElTy> Prefix(TheList.begin(), TheList.begin() + Mid);
 | |
|       std::vector<ElTy> Suffix(TheList.begin() + Mid, TheList.end());
 | |
| 
 | |
|       Expected<TestResult> Result = doTest(Prefix, Suffix);
 | |
|       if (Error E = Result.takeError())
 | |
|         return std::move(E);
 | |
|       switch (*Result) {
 | |
|       case KeepSuffix:
 | |
|         // The property still holds.  We can just drop the prefix elements, and
 | |
|         // shorten the list to the "kept" elements.
 | |
|         TheList.swap(Suffix);
 | |
|         MidTop = TheList.size();
 | |
|         // Reset progress treshold and progress counter
 | |
|         MaxIterations = MaxIterationsWithoutProgress;
 | |
|         NumOfIterationsWithoutProgress = 0;
 | |
|         break;
 | |
|       case KeepPrefix:
 | |
|         // The predicate still holds, shorten the list to the prefix elements.
 | |
|         TheList.swap(Prefix);
 | |
|         MidTop = TheList.size();
 | |
|         // Reset progress treshold and progress counter
 | |
|         MaxIterations = MaxIterationsWithoutProgress;
 | |
|         NumOfIterationsWithoutProgress = 0;
 | |
|         break;
 | |
|       case NoFailure:
 | |
|         // Otherwise the property doesn't hold.  Some of the elements we removed
 | |
|         // must be necessary to maintain the property.
 | |
|         MidTop = Mid;
 | |
|         NumOfIterationsWithoutProgress++;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Probability of backjumping from the trimming loop back to the binary
 | |
|     // split reduction loop.
 | |
|     const int BackjumpProbability = 10;
 | |
| 
 | |
|     // Okay, we trimmed as much off the top and the bottom of the list as we
 | |
|     // could.  If there is more than two elements in the list, try deleting
 | |
|     // interior elements and testing that.
 | |
|     //
 | |
|     if (TheList.size() > 2) {
 | |
|       bool Changed = true;
 | |
|       std::vector<ElTy> EmptyList;
 | |
|       unsigned TrimIterations = 0;
 | |
|       while (Changed) { // Trimming loop.
 | |
|         Changed = false;
 | |
| 
 | |
|         // If the binary split reduction loop made an unfortunate sequence of
 | |
|         // splits, the trimming loop might be left off with a huge number of
 | |
|         // remaining elements (large search space). Backjumping out of that
 | |
|         // search space and attempting a different split can significantly
 | |
|         // improve the convergence speed.
 | |
|         if (std::rand() % 100 < BackjumpProbability)
 | |
|           goto Backjump;
 | |
| 
 | |
|         for (unsigned i = 1; i < TheList.size() - 1; ++i) {
 | |
|           // Check interior elts
 | |
|           if (BugpointIsInterrupted) {
 | |
|             errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";
 | |
|             return true;
 | |
|           }
 | |
| 
 | |
|           std::vector<ElTy> TestList(TheList);
 | |
|           TestList.erase(TestList.begin() + i);
 | |
| 
 | |
|           Expected<TestResult> Result = doTest(EmptyList, TestList);
 | |
|           if (Error E = Result.takeError())
 | |
|             return std::move(E);
 | |
|           if (*Result == KeepSuffix) {
 | |
|             // We can trim down the list!
 | |
|             TheList.swap(TestList);
 | |
|             --i; // Don't skip an element of the list
 | |
|             Changed = true;
 | |
|           }
 | |
|         }
 | |
|         if (TrimIterations >= MaxTrimIterationsWithoutBackJump)
 | |
|           break;
 | |
|         TrimIterations++;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return true; // there are some failure and we've narrowed them down
 | |
|   }
 | |
| };
 | |
| 
 | |
| } // End llvm namespace
 | |
| 
 | |
| #endif
 |