Merge pull request #1321 from gottesmm/immutable_pointer_set_feedback

Immutable Pointer Set Fixes
This commit is contained in:
Michael Gottesman
2016-02-16 09:45:17 -08:00
5 changed files with 158 additions and 138 deletions

View File

@@ -55,100 +55,6 @@
#include <algorithm>
#include <type_traits>
// Include these here for now to reduce build times while I am testing. Putting
// things into STLExtras causes pretty much everything to recompile.
namespace swift {
/// @{
/// The equivalent of std::for_each, but visits the set union of two sorted
/// lists without allocating additional memory.
template <typename InputIt1, typename InputIt2, typename BinaryFunction>
inline void set_union_for_each(InputIt1 I1, InputIt1 E1, InputIt2 I2,
InputIt2 E2, BinaryFunction f) {
while (true) {
// If we have reached the end of either list, visit the rest of the other
// list, We do not need to worry about duplicates since each array we know
// is unique.
if (I1 == E1) {
std::for_each(I2, E2, f);
return;
}
if (I2 == E2) {
std::for_each(I1, E1, f);
return;
}
// If I1 < I2, then visit I1 and continue.
if (*I1 < *I2) {
f(*I1);
++I1;
continue;
}
// If I2 < I1, visit I2 and continue.
if (*I2 < *I1) {
f(*I2);
++I2;
continue;
}
// Otherwise, we know that I1 and I2 equal. We know that we can only have
// one of each element in each list, so we can just visit I1 and continue.
f(*I1);
++I1;
++I2;
}
}
/// A container adapter for set_union_for_each.
template <typename Container1, typename Container2, typename BinaryFunction>
inline void set_union_for_each(Container1 &&C1, Container2 &&C2,
BinaryFunction f) {
set_union_for_each(C1.begin(), C1.end(), C2.begin(), C2.end(), f);
}
/// @}
/// Returns true if [II, IE) is a sorted and uniqued array. Returns false
/// otherwise.
template <typename IterTy> bool is_sorted_and_uniqued(IterTy II, IterTy IE) {
// The empty list is always sorted and uniqued.
if (II == IE)
return true;
// The list of one element is always sorted and uniqued.
auto LastI = II;
++II;
if (II == IE)
return true;
// Otherwise, until we reach the end of the list...
while (II != IE) {
// If LastI is greater than II then we know that our array is not sorted. If
// LastI equals II, then we know that our array is not unique. If both of
// those are conditions are false, then visit the next iterator element.
if (*LastI < *II) {
LastI = II;
++II;
continue;
}
// Return false otherwise.
return false;
}
// Success!
return true;
}
template <typename Container> bool is_sorted_and_uniqued(Container &&C) {
return is_sorted_and_uniqued(C.begin(), C.end());
}
} // end swift namespace
namespace swift {
template <typename PtrTy> class ImmutablePointerSetFactory;
@@ -175,19 +81,18 @@ public:
ImmutablePointerSet &operator=(ImmutablePointerSet &&) = default;
bool operator==(const ImmutablePointerSet<T> &P) const {
// If both are empty, they must be equivalent.
if (empty() && P.empty())
return true;
// Ok, at least one is non-empty. If either are empty at this point, then we
// are comparing a non-empty set with an empty set, i.e. they do not equal.
if (empty() || P.empty())
// If this and P have different sizes, we can not be equivalent.
if (size() != P.size())
return false;
// Ok, both sets are not empty. Compare their profiles.
llvm::FoldingSetNodeID ID1, ID2;
Profile(ID1);
P.Profile(ID2);
return ID1 == ID2;
// Ok, we now know that both have the same size. If one is empty, the other
// must be as well, implying equality.
if (empty())
return true;
// Ok, both sets are not empty and the same number of elements. Compare
// element wise.
return std::equal(begin(), end(), P.begin());
}
bool operator!=(const ImmutablePointerSet<T> &P) const {
@@ -221,13 +126,13 @@ public:
}
}
ImmutablePointerSet<T> *concat(ImmutablePointerSet<T> *Other) {
ImmutablePointerSet<T> *merge(ImmutablePointerSet<T> *Other) {
if (empty())
return Other;
if (Other->empty())
return this;
assert(Other->ParentFactory.get() == ParentFactory.get());
return ParentFactory.get()->concat(this, Other);
return ParentFactory.get()->merge(this, Other);
}
};
@@ -294,7 +199,7 @@ public:
return NewNode;
}
PtrSet *concat(PtrSet *S1, ArrayRef<PtrTy> S2) {
PtrSet *merge(PtrSet *S1, ArrayRef<PtrTy> S2) {
if (S1->empty())
return get(S2);
@@ -340,7 +245,7 @@ public:
return NewNode;
}
PtrSet *concat(PtrSet *S1, PtrSet *S2) {
PtrSet *merge(PtrSet *S1, PtrSet *S2) {
// If either S1 or S2 are the empty PtrSet, just return S2 or S1.
if (S1->empty())
return S2;

View File

@@ -19,6 +19,7 @@
#include "swift/Basic/LLVM.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
#include <cassert>
#include <functional>
@@ -101,6 +102,84 @@ inline void for_each3(const Container1 &c1, const Container2 &c2,
for_each3(c1.begin(), c1.end(), c2.begin(), c3.begin(), f);
}
/// The equivalent of std::for_each, but visits the set union of two sorted
/// lists without allocating additional memory.
///
/// This has the following requirements:
///
/// 1. The ranges must be sorted.
/// 2. The elements must have the same type.
/// 3. There are no duplicate elements.
/// 4. All elements must be comparable with std::less.
template <typename InputIt1, typename InputIt2, typename BinaryFunction>
inline void set_union_for_each(InputIt1 I1, InputIt1 E1, InputIt2 I2,
InputIt2 E2, BinaryFunction f) {
static_assert(
std::is_same<
typename std::iterator_traits<InputIt1>::value_type,
typename std::iterator_traits<InputIt2>::value_type
>::value,
"Expected both iterator types to have the same underlying value type");
using RefTy = typename std::iterator_traits<InputIt1>::reference;
while (true) {
// If we have reached the end of either list, visit the rest of the other
// list, We do not need to worry about duplicates since each array we know
// is unique.
if (I1 == E1) {
std::for_each(I2, E2, f);
return;
}
if (I2 == E2) {
std::for_each(I1, E1, f);
return;
}
// If I1 < I2, then visit I1 and continue.
if (std::less<RefTy>()(*I1, *I2)) {
f(*I1);
++I1;
continue;
}
// If I2 < I1, visit I2 and continue.
if (std::less<RefTy>()(*I2, *I1)) {
f(*I2);
++I2;
continue;
}
// Otherwise, we know that I1 and I2 equal. We know that we can only have
// one of each element in each list, so we can just visit I1 and continue.
f(*I1);
++I1;
++I2;
}
}
/// A container adapter for set_union_for_each.
///
/// To see the requirements upon the containers, please see the iterator based
/// set_union_for_each.
template <typename Container1, typename Container2, typename UnaryFunction>
inline void set_union_for_each(const Container1 &C1, const Container2 &C2,
UnaryFunction f) {
// Make sure that our iterators have the same value type.
static_assert(
std::is_same<
typename std::iterator_traits<
typename Container1::iterator
>::value_type,
typename std::iterator_traits<
typename Container2::iterator
>::value_type
>::value,
"Expected both containers to have the same iterator value type");
set_union_for_each(C1.begin(), C1.end(), C2.begin(), C2.end(), f);
}
/// @}
/// A range of iterators.
@@ -573,6 +652,45 @@ void sortUnique(
C.erase(std::unique(C.begin(), C.end()), C.end());
}
/// Returns true if [II, IE) is a sorted and uniqued array. Returns false
/// otherwise.
template <typename IterTy>
inline bool is_sorted_and_uniqued(IterTy II, IterTy IE) {
using RefTy = typename std::iterator_traits<IterTy>::reference;
// The empty list is always sorted and uniqued.
if (II == IE)
return true;
// The list of one element is always sorted and uniqued.
auto LastI = II;
++II;
if (II == IE)
return true;
// Otherwise, until we reach the end of the list...
while (II != IE) {
// If LastI is greater than II then we know that our array is not sorted. If
// LastI equals II, then we know that our array is not unique. If both of
// those are conditions are false, then visit the next iterator element.
if (std::greater_equal<RefTy>()(*LastI, *II)) {
// Return false otherwise.
return false;
}
LastI = II;
++II;
}
// Success!
return true;
}
template <typename Container>
inline bool is_sorted_and_uniqued(const Container &C) {
return is_sorted_and_uniqued(C.begin(), C.end());
}
} // end namespace swift
#endif

View File

@@ -160,7 +160,7 @@ bool RCStateTransition::merge(const RCStateTransition &Other) {
if (!isMutator())
return true;
Mutators = Mutators->concat(Other.Mutators);
Mutators = Mutators->merge(Other.Mutators);
return true;
}

View File

@@ -340,7 +340,7 @@ bool BottomUpRefCountState::merge(const BottomUpRefCountState &Other) {
Partial |= Other.Partial;
if (*InsertPts != *Other.InsertPts) {
Partial = true;
InsertPts = InsertPts->concat(Other.InsertPts);
InsertPts = InsertPts->merge(Other.InsertPts);
}
DEBUG(llvm::dbgs() << " Partial: " << (Partial ? "yes" : "no")
@@ -628,7 +628,7 @@ bool TopDownRefCountState::handleDecrement(
switch (LatState) {
case LatticeState::Incremented:
LatState = LatticeState::MightBeDecremented;
InsertPts = SetFactory.concat(InsertPts, NewInsertPt);
InsertPts = SetFactory.merge(InsertPts, NewInsertPt);
return true;
case LatticeState::None:
case LatticeState::MightBeDecremented:
@@ -793,7 +793,7 @@ bool TopDownRefCountState::merge(const TopDownRefCountState &Other) {
Partial |= Other.Partial;
if (*InsertPts != *Other.InsertPts) {
Partial = true;
InsertPts = InsertPts->concat(Other.InsertPts);
InsertPts = InsertPts->merge(Other.InsertPts);
}
DEBUG(llvm::dbgs() << " Partial: " << (Partial ? "yes" : "no")

View File

@@ -2,7 +2,6 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Allocator.h"
#include "gtest/gtest.h"
#include <vector>
using namespace swift;
@@ -13,32 +12,32 @@ TEST(ImmutableSortedSet, OneElementSets) {
unsigned *ptr1 = (unsigned *)3;
auto *OneEltSet1 = F.get(ptr1);
EXPECT_EQ(OneEltSet1, F.get(ptr1));
EXPECT_EQ(OneEltSet1, F.concat(OneEltSet1, OneEltSet1));
EXPECT_EQ(OneEltSet1, F.merge(OneEltSet1, OneEltSet1));
unsigned *ptr2 = (unsigned *)2;
auto *OneEltSet2 = F.get(ptr2);
EXPECT_EQ(OneEltSet2, F.get(ptr2));
EXPECT_EQ(OneEltSet2, F.concat(OneEltSet2, OneEltSet2));
EXPECT_EQ(OneEltSet2, F.merge(OneEltSet2, OneEltSet2));
EXPECT_NE(OneEltSet2, OneEltSet1);
auto *Concat1 = F.concat(OneEltSet1, OneEltSet2);
auto *Concat2 = F.concat(OneEltSet2, OneEltSet1);
EXPECT_NE(OneEltSet1, Concat1);
EXPECT_NE(OneEltSet2, Concat1);
EXPECT_EQ(Concat1, Concat2);
EXPECT_EQ(Concat1, F.concat(Concat1, Concat1));
EXPECT_EQ(Concat2, F.concat(Concat2, Concat2));
EXPECT_EQ(Concat1, F.concat(Concat1, OneEltSet1));
EXPECT_EQ(Concat1, F.concat(Concat1, OneEltSet2));
auto *Merge1 = F.merge(OneEltSet1, OneEltSet2);
auto *Merge2 = F.merge(OneEltSet2, OneEltSet1);
EXPECT_NE(OneEltSet1, Merge1);
EXPECT_NE(OneEltSet2, Merge1);
EXPECT_EQ(Merge1, Merge2);
EXPECT_EQ(Merge1, F.merge(Merge1, Merge1));
EXPECT_EQ(Merge2, F.merge(Merge2, Merge2));
EXPECT_EQ(Merge1, F.merge(Merge1, OneEltSet1));
EXPECT_EQ(Merge1, F.merge(Merge1, OneEltSet2));
EXPECT_EQ(Concat1->size(), 2U);
EXPECT_FALSE(Concat1->empty());
EXPECT_EQ(*Concat1->begin(), (unsigned *)2);
EXPECT_EQ(*std::next(Concat1->begin()), (unsigned *)3);
EXPECT_EQ(Merge1->size(), 2U);
EXPECT_FALSE(Merge1->empty());
EXPECT_EQ(*Merge1->begin(), (unsigned *)2);
EXPECT_EQ(*std::next(Merge1->begin()), (unsigned *)3);
EXPECT_EQ(F.getEmptySet(), F.getEmptySet());
EXPECT_EQ(OneEltSet1, F.concat(F.getEmptySet(), OneEltSet1));
EXPECT_EQ(OneEltSet1, F.concat(OneEltSet1, F.getEmptySet()));
EXPECT_EQ(OneEltSet1, F.merge(F.getEmptySet(), OneEltSet1));
EXPECT_EQ(OneEltSet1, F.merge(OneEltSet1, F.getEmptySet()));
}
TEST(ImmutablePointerSet, MultipleElementSets) {
@@ -50,9 +49,7 @@ TEST(ImmutablePointerSet, MultipleElementSets) {
unsigned *Ptr3 = (unsigned *)3;
unsigned *Ptr4 = (unsigned *)5;
unsigned *Ptr5 = (unsigned *)6;
std::vector<unsigned *> Data1;
Data1.push_back(Ptr1);
Data1.push_back(Ptr2);
ArrayRef<unsigned *> Data1 = {Ptr1, Ptr2};
auto *TwoEltSet = F.get(Data1);
EXPECT_FALSE(TwoEltSet->empty());
@@ -63,7 +60,7 @@ TEST(ImmutablePointerSet, MultipleElementSets) {
EXPECT_FALSE(TwoEltSet->count(Ptr4));
EXPECT_FALSE(TwoEltSet->count(Ptr5));
auto *ThreeEltSet = F.concat(F.get(Ptr4), TwoEltSet);
auto *ThreeEltSet = F.merge(F.get(Ptr4), TwoEltSet);
EXPECT_FALSE(ThreeEltSet->empty());
EXPECT_EQ(ThreeEltSet->size(), 3u);
EXPECT_NE(*ThreeEltSet, *TwoEltSet);
@@ -72,9 +69,9 @@ TEST(ImmutablePointerSet, MultipleElementSets) {
EXPECT_TRUE(ThreeEltSet->count(Ptr3));
EXPECT_TRUE(ThreeEltSet->count(Ptr4));
EXPECT_FALSE(ThreeEltSet->count(Ptr5));
EXPECT_EQ(ThreeEltSet, F.concat(TwoEltSet, ThreeEltSet));
EXPECT_EQ(ThreeEltSet, F.merge(TwoEltSet, ThreeEltSet));
std::vector<unsigned *> Data2 = {Ptr3, Ptr4, Ptr5};
ArrayRef<unsigned *> Data2 = {Ptr3, Ptr4, Ptr5};
auto *PartialOverlapSet = F.get(Data2);
EXPECT_FALSE(PartialOverlapSet->empty());
EXPECT_EQ(PartialOverlapSet->size(), 3u);
@@ -86,7 +83,7 @@ TEST(ImmutablePointerSet, MultipleElementSets) {
EXPECT_NE(*PartialOverlapSet, *ThreeEltSet);
EXPECT_NE(*PartialOverlapSet, *TwoEltSet);
auto *MixOfThreeAndPartialOverlap = ThreeEltSet->concat(PartialOverlapSet);
auto *MixOfThreeAndPartialOverlap = ThreeEltSet->merge(PartialOverlapSet);
EXPECT_FALSE(MixOfThreeAndPartialOverlap->empty());
EXPECT_EQ(MixOfThreeAndPartialOverlap->size(), 4u);
EXPECT_TRUE(MixOfThreeAndPartialOverlap->count(Ptr1));