| 1 | //===- Delta.h - Delta Debugging Algorithm Implementation -------*- 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 file contains the implementation for the Delta Debugging Algorithm: |
| 10 | // it splits a given set of Targets (i.e. Functions, Instructions, BBs, etc.) |
| 11 | // into chunks and tries to reduce the number chunks that are interesting. |
| 12 | // |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
| 15 | #ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_DELTA_H |
| 16 | #define LLVM_TOOLS_LLVM_REDUCE_DELTAS_DELTA_H |
| 17 | |
| 18 | #include "ReducerWorkItem.h" |
| 19 | #include "llvm/ADT/ArrayRef.h" |
| 20 | #include "llvm/Support/raw_ostream.h" |
| 21 | #include <functional> |
| 22 | #include <utility> |
| 23 | |
| 24 | namespace llvm { |
| 25 | |
| 26 | class TestRunner; |
| 27 | struct DeltaPass; |
| 28 | |
| 29 | struct Chunk { |
| 30 | int Begin; |
| 31 | int End; |
| 32 | |
| 33 | /// Helper function to verify if a given Target-index is inside the Chunk |
| 34 | bool contains(int Index) const { return Index >= Begin && Index <= End; } |
| 35 | |
| 36 | void print() const { |
| 37 | errs() << '[' << Begin; |
| 38 | if (End - Begin != 0) |
| 39 | errs() << ',' << End; |
| 40 | errs() << ']'; |
| 41 | } |
| 42 | |
| 43 | /// Operator when populating CurrentChunks in Generic Delta Pass |
| 44 | friend bool operator!=(const Chunk &C1, const Chunk &C2) { |
| 45 | return C1.Begin != C2.Begin || C1.End != C2.End; |
| 46 | } |
| 47 | |
| 48 | friend bool operator==(const Chunk &C1, const Chunk &C2) { |
| 49 | return C1.Begin == C2.Begin && C1.End == C2.End; |
| 50 | } |
| 51 | |
| 52 | /// Operator used for sets |
| 53 | friend bool operator<(const Chunk &C1, const Chunk &C2) { |
| 54 | return std::tie(args: C1.Begin, args: C1.End) < std::tie(args: C2.Begin, args: C2.End); |
| 55 | } |
| 56 | }; |
| 57 | |
| 58 | template<> |
| 59 | struct DenseMapInfo<Chunk> { |
| 60 | static inline Chunk getEmptyKey() { |
| 61 | return {.Begin: DenseMapInfo<int>::getEmptyKey(), |
| 62 | .End: DenseMapInfo<int>::getEmptyKey()}; |
| 63 | } |
| 64 | |
| 65 | static inline Chunk getTombstoneKey() { |
| 66 | return {.Begin: DenseMapInfo<int>::getTombstoneKey(), |
| 67 | .End: DenseMapInfo<int>::getTombstoneKey()}; |
| 68 | } |
| 69 | |
| 70 | static unsigned getHashValue(const Chunk Val) { |
| 71 | std::pair<int, int> PairVal = std::make_pair(x: Val.Begin, y: Val.End); |
| 72 | return DenseMapInfo<std::pair<int, int>>::getHashValue(PairVal); |
| 73 | } |
| 74 | |
| 75 | static bool isEqual(const Chunk LHS, const Chunk RHS) { |
| 76 | return LHS == RHS; |
| 77 | } |
| 78 | }; |
| 79 | |
| 80 | |
| 81 | /// Provides opaque interface for querying into ChunksToKeep without having to |
| 82 | /// actually understand what is going on. |
| 83 | class Oracle { |
| 84 | /// Out of all the features that we promised to be, |
| 85 | /// how many have we already processed? |
| 86 | int Index = 0; |
| 87 | |
| 88 | /// The actual workhorse, contains the knowledge whether or not |
| 89 | /// some particular feature should be preserved this time. |
| 90 | ArrayRef<Chunk> ChunksToKeep; |
| 91 | |
| 92 | public: |
| 93 | explicit Oracle(ArrayRef<Chunk> ChunksToKeep) : ChunksToKeep(ChunksToKeep) {} |
| 94 | |
| 95 | /// Should be called for each feature on which we are operating. |
| 96 | /// Name is self-explanatory - if returns true, then it should be preserved. |
| 97 | bool shouldKeep() { |
| 98 | if (ChunksToKeep.empty()) { |
| 99 | ++Index; |
| 100 | return false; // All further features are to be discarded. |
| 101 | } |
| 102 | |
| 103 | // Does the current (front) chunk contain such a feature? |
| 104 | bool ShouldKeep = ChunksToKeep.front().contains(Index); |
| 105 | |
| 106 | // Is this the last feature in the chunk? |
| 107 | if (ChunksToKeep.front().End == Index) |
| 108 | ChunksToKeep = ChunksToKeep.drop_front(); // Onto next chunk. |
| 109 | |
| 110 | ++Index; |
| 111 | |
| 112 | return ShouldKeep; |
| 113 | } |
| 114 | |
| 115 | int count() { return Index; } |
| 116 | }; |
| 117 | |
| 118 | using ReductionFunc = function_ref<void(Oracle &, ReducerWorkItem &)>; |
| 119 | |
| 120 | /// This function implements the Delta Debugging algorithm, it receives a |
| 121 | /// number of Targets (e.g. Functions, Instructions, Basic Blocks, etc.) and |
| 122 | /// splits them in half; these chunks of targets are then tested while ignoring |
| 123 | /// one chunk, if a chunk is proven to be uninteresting (i.e. fails the test) |
| 124 | /// it is removed from consideration. The algorithm will attempt to split the |
| 125 | /// Chunks in half and start the process again until it can't split chunks |
| 126 | /// anymore. |
| 127 | /// |
| 128 | /// This function is intended to be called by each specialized delta pass (e.g. |
| 129 | /// RemoveFunctions) and receives three key parameters: |
| 130 | /// * Test: The main TestRunner instance which is used to run the provided |
| 131 | /// interesting-ness test, as well as to store and access the reduced Program. |
| 132 | /// * ExtractChunksFromModule: A function used to tailor the main program so it |
| 133 | /// only contains Targets that are inside Chunks of the given iteration. |
| 134 | /// Note: This function is implemented by each specialized Delta pass |
| 135 | /// |
| 136 | /// Other implementations of the Delta Debugging algorithm can also be found in |
| 137 | /// the CReduce, Delta, and Lithium projects. |
| 138 | void runDeltaPass(TestRunner &Test, const DeltaPass &Pass); |
| 139 | } // namespace llvm |
| 140 | |
| 141 | #endif |
| 142 | |