mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge pull request #1321 from gottesmm/immutable_pointer_set_feedback
Immutable Pointer Set Fixes
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user