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 | |