summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/llvm/ADT/DeltaAlgorithm.h89
-rw-r--r--lib/Support/CMakeLists.txt1
-rw-r--r--lib/Support/DeltaAlgorithm.cpp110
-rw-r--r--unittests/ADT/DeltaAlgorithmTest.cpp96
4 files changed, 296 insertions, 0 deletions
diff --git a/include/llvm/ADT/DeltaAlgorithm.h b/include/llvm/ADT/DeltaAlgorithm.h
new file mode 100644
index 00000000000..0cf7c7c4665
--- /dev/null
+++ b/include/llvm/ADT/DeltaAlgorithm.h
@@ -0,0 +1,89 @@
+//===--- DeltaAlgorithm.h - A Set Minimization Algorithm -------*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_DELTAALGORITHM_H
+#define LLVM_ADT_DELTAALGORITHM_H
+
+#include <vector>
+#include <set>
+
+namespace llvm {
+
+/// DeltaAlgorithm - Implements the delta debugging algorithm (A. Zeller '99)
+/// for minimizing arbitrary sets using a predicate function.
+///
+/// The result of the algorithm is a subset of the input change set which is
+/// guaranteed to satisfy the predicate, assuming that the input set did. For
+/// well formed predicates, the result set is guaranteed to be such that
+/// removing any single element would falsify the predicate.
+///
+/// For best results the predicate function *should* (but need not) satisfy
+/// certain properties, in particular:
+/// (1) The predicate should return false on an empty set and true on the full
+/// set.
+/// (2) If the predicate returns true for a set of changes, it should return
+/// true for all supersets of that set.
+///
+/// It is not an error to provide a predicate that does not satisfy these
+/// requirements, and the algorithm will generally produce reasonable
+/// results. However, it may run substantially more tests than with a good
+/// predicate.
+class DeltaAlgorithm {
+public:
+ typedef unsigned change_ty;
+ // FIXME: Use a decent data structure.
+ typedef std::set<change_ty> changeset_ty;
+ typedef std::vector<changeset_ty> changesetlist_ty;
+
+private:
+ /// Cache of failed test results. Successful test results are never cached
+ /// since we always reduce following a success.
+ std::set<changeset_ty> FailedTestsCache;
+
+ /// GetTestResult - Get the test result for the \arg Changes from the
+ /// cache, executing the test if necessary.
+ ///
+ /// \param Changes - The change set to test.
+ /// \return - The test result.
+ bool GetTestResult(const changeset_ty &Changes);
+
+ /// Split - Partition a set of changes \arg Sinto one or two subsets.
+ void Split(const changeset_ty &S, changesetlist_ty &Res);
+
+ /// Delta - Minimize a set of \arg Changes which has been partioned into
+ /// smaller sets, by attempting to remove individual subsets.
+ changeset_ty Delta(const changeset_ty &Changes,
+ const changesetlist_ty &Sets);
+
+ /// Search - Search for a subset (or subsets) in \arg Sets which can be
+ /// removed from \arg Changes while still satisfying the predicate.
+ ///
+ /// \param Res - On success, a subset of Changes which satisfies the
+ /// predicate.
+ /// \return - True on success.
+ bool Search(const changeset_ty &Changes, const changesetlist_ty &Sets,
+ changeset_ty &Res);
+
+protected:
+ /// UpdatedSearchState - Callback used when the search state changes.
+ virtual void UpdatedSearchState(const changeset_ty &Changes,
+ const changesetlist_ty &Sets) {}
+
+ /// ExecuteOneTest - Execute a single test predicate on the change set \arg S.
+ virtual bool ExecuteOneTest(const changeset_ty &S) = 0;
+
+public:
+ /// Run - Minimize the set \arg Changes by executing \see ExecuteOneTest() on
+ /// subsets of changes and returning the smallest set which still satisfies
+ /// the test predicate.
+ changeset_ty Run(const changeset_ty &Changes);
+};
+
+} // end namespace llvm
+
+#endif
diff --git a/lib/Support/CMakeLists.txt b/lib/Support/CMakeLists.txt
index cd355ffe360..ac736dc66f7 100644
--- a/lib/Support/CMakeLists.txt
+++ b/lib/Support/CMakeLists.txt
@@ -6,6 +6,7 @@ add_llvm_library(LLVMSupport
CommandLine.cpp
ConstantRange.cpp
Debug.cpp
+ DeltaAlgorithm.cpp
Dwarf.cpp
ErrorHandling.cpp
FileUtilities.cpp
diff --git a/lib/Support/DeltaAlgorithm.cpp b/lib/Support/DeltaAlgorithm.cpp
new file mode 100644
index 00000000000..9aabd8d1a01
--- /dev/null
+++ b/lib/Support/DeltaAlgorithm.cpp
@@ -0,0 +1,110 @@
+//===--- DeltaAlgorithm.h - A Set Minimization Algorithm -------*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/DeltaAlgorithm.h"
+#include <algorithm>
+using namespace llvm;
+
+bool DeltaAlgorithm::GetTestResult(const changeset_ty &Changes) {
+ if (FailedTestsCache.count(Changes))
+ return false;
+
+ bool Result = ExecuteOneTest(Changes);
+ if (!Result)
+ FailedTestsCache.insert(Changes);
+
+ return Result;
+}
+
+void DeltaAlgorithm::Split(const changeset_ty &S, changesetlist_ty &Res) {
+ // FIXME: Allow clients to provide heuristics for improved splitting.
+
+ // FIXME: This is really slow.
+ changeset_ty LHS, RHS;
+ unsigned idx = 0;
+ for (changeset_ty::const_iterator it = S.begin(),
+ ie = S.end(); it != ie; ++it, ++idx)
+ ((idx & 1) ? LHS : RHS).insert(*it);
+ if (!LHS.empty())
+ Res.push_back(LHS);
+ if (!RHS.empty())
+ Res.push_back(RHS);
+}
+
+DeltaAlgorithm::changeset_ty
+DeltaAlgorithm::Delta(const changeset_ty &Changes,
+ const changesetlist_ty &Sets) {
+ // Invariant: union(Res) == Changes
+ UpdatedSearchState(Changes, Sets);
+
+ // If there is nothing left we can remove, we are done.
+ if (Sets.size() <= 1)
+ return Changes;
+
+ // Look for a passing subset.
+ changeset_ty Res;
+ if (Search(Changes, Sets, Res))
+ return Res;
+
+ // Otherwise, partition the sets if possible; if not we are done.
+ changesetlist_ty SplitSets;
+ for (changesetlist_ty::const_iterator it = Sets.begin(),
+ ie = Sets.end(); it != ie; ++it)
+ Split(*it, SplitSets);
+ if (SplitSets.size() == Sets.size())
+ return Changes;
+
+ return Delta(Changes, SplitSets);
+}
+
+bool DeltaAlgorithm::Search(const changeset_ty &Changes,
+ const changesetlist_ty &Sets,
+ changeset_ty &Res) {
+ // FIXME: Parallelize.
+ for (changesetlist_ty::const_iterator it = Sets.begin(),
+ ie = Sets.end(); it != ie; ++it) {
+ // If the test passes on this subset alone, recurse.
+ if (GetTestResult(*it)) {
+ changesetlist_ty Sets;
+ Split(*it, Sets);
+ Res = Delta(*it, Sets);
+ return true;
+ }
+
+ // Otherwise, if we have more than two sets, see if test passes on the
+ // complement.
+ if (Sets.size() > 2) {
+ // FIXME: This is really slow.
+ changeset_ty Complement;
+ std::set_difference(
+ Changes.begin(), Changes.end(), it->begin(), it->end(),
+ std::insert_iterator<changeset_ty>(Complement, Complement.begin()));
+ if (GetTestResult(Complement)) {
+ changesetlist_ty ComplementSets;
+ ComplementSets.insert(ComplementSets.end(), Sets.begin(), it);
+ ComplementSets.insert(ComplementSets.end(), it + 1, Sets.end());
+ Res = Delta(Complement, ComplementSets);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+DeltaAlgorithm::changeset_ty DeltaAlgorithm::Run(const changeset_ty &Changes) {
+ // Check empty set first to quickly find poor test functions.
+ if (GetTestResult(changeset_ty()))
+ return changeset_ty();
+
+ // Otherwise run the real delta algorithm.
+ changesetlist_ty Sets;
+ Split(Changes, Sets);
+
+ return Delta(Changes, Sets);
+}
diff --git a/unittests/ADT/DeltaAlgorithmTest.cpp b/unittests/ADT/DeltaAlgorithmTest.cpp
new file mode 100644
index 00000000000..362892207f5
--- /dev/null
+++ b/unittests/ADT/DeltaAlgorithmTest.cpp
@@ -0,0 +1,96 @@
+//===- llvm/unittest/ADT/DeltaAlgorithmTest.cpp ---------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+#include "llvm/ADT/DeltaAlgorithm.h"
+#include <algorithm>
+#include <cstdarg>
+using namespace llvm;
+
+std::ostream &operator<<(std::ostream &OS,
+ const std::set<unsigned> &S) {
+ OS << "{";
+ for (std::set<unsigned>::const_iterator it = S.begin(),
+ ie = S.end(); it != ie; ++it) {
+ if (it != S.begin())
+ OS << ",";
+ OS << *it;
+ }
+ OS << "}";
+ return OS;
+}
+
+namespace {
+
+class FixedDeltaAlgorithm : public DeltaAlgorithm {
+ changeset_ty FailingSet;
+ unsigned NumTests;
+
+protected:
+ virtual bool ExecuteOneTest(const changeset_ty &Changes) {
+ ++NumTests;
+ return std::includes(Changes.begin(), Changes.end(),
+ FailingSet.begin(), FailingSet.end());
+ }
+
+public:
+ FixedDeltaAlgorithm(const changeset_ty &_FailingSet)
+ : FailingSet(_FailingSet),
+ NumTests(0) {}
+
+ unsigned getNumTests() const { return NumTests; }
+};
+
+std::set<unsigned> fixed_set(unsigned N, ...) {
+ std::set<unsigned> S;
+ va_list ap;
+ va_start(ap, N);
+ for (unsigned i = 0; i != N; ++i)
+ S.insert(va_arg(ap, unsigned));
+ va_end(ap);
+ return S;
+}
+
+std::set<unsigned> range(unsigned Start, unsigned End) {
+ std::set<unsigned> S;
+ while (Start != End)
+ S.insert(Start++);
+ return S;
+}
+
+std::set<unsigned> range(unsigned N) {
+ return range(0, N);
+}
+
+TEST(DeltaAlgorithmTest, Basic) {
+ // P = {3,5,7} \in S
+ // [0, 20) should minimize to {3,5,7} in a reasonable number of tests.
+ std::set<unsigned> Fails = fixed_set(3, 3, 5, 7);
+ FixedDeltaAlgorithm FDA(Fails);
+ EXPECT_EQ(fixed_set(3, 3, 5, 7), FDA.Run(range(20)));
+ EXPECT_GE(33U, FDA.getNumTests());
+
+ // P = {3,5,7} \in S
+ // [10, 20) should minimize to [10,20)
+ EXPECT_EQ(range(10,20), FDA.Run(range(10,20)));
+
+ // P = [0,4) \in S
+ // [0, 4) should minimize to [0,4) in 11 tests.
+ //
+ // 11 = |{ {},
+ // {0}, {1}, {2}, {3},
+ // {1, 2, 3}, {0, 2, 3}, {0, 1, 3}, {0, 1, 2},
+ // {0, 1}, {2, 3} }|
+ FDA = FixedDeltaAlgorithm(range(10));
+ EXPECT_EQ(range(4), FDA.Run(range(4)));
+ EXPECT_EQ(11U, FDA.getNumTests());
+}
+
+}
+