Delta.h 4.8 KB

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