/* * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #include #include #include namespace WTF { // A RangeSet is a set of numerical ranges. A value belongs to the set if it is within any of the // ranges. A range belongs to the set if every value in the range belongs to the set. A range overlaps // the set if any value in the range belongs to the set. You can add ranges and query range // membership. The internal representation is a list of ranges that gets periodically compacted. This // representation is optimal so long as the number of distinct ranges tends to be small, and the // number of range sets tends to be small as well. This works reasonably well in a bunch of compiler // algorithms, where the top range ends up being used a lot. // // The initial user of this is JSC::B3::HeapRange, which is used to perform alias analysis. You can // model new users on that class. Basically, you need to define: // // T::Type - the type of the members of the range. HeapRange uses unsigned. // T(T::Type begin, T::Type end) - construct a new range. // T::Type T::begin() const - instance method giving the inclusive beginning of the range. // T::Type T::end() const - instance method giving the exclusive end of the range. // void T::dump(PrintStream&) const - some kind of dumping. template class RangeSet final { WTF_MAKE_FAST_ALLOCATED; public: typedef RangeType Range; typedef typename Range::Type Type; typedef Vector VectorType; RangeSet() { } ~RangeSet() { } void add(const Range& range) { if (range.begin() == range.end()) return; // We expect the range set to become top in a lot of cases. We also expect the same range to // be added repeatedly. That's why this is here. if (!m_ranges.isEmpty() && subsumesNonEmpty(m_ranges.last(), range)) return; m_isCompact = false; // We append without compacting only if doing so is guaranteed not to resize the vector. // FIXME: This heuristic is almost certainly wrong, because we don't control the capacity. I // think that this means that we will sometimes be rage-compacting when we are just shy of the // capacity. // https://bugs.webkit.org/show_bug.cgi?id=170308 if (m_ranges.size() + 1 < m_ranges.capacity()) { m_ranges.append(range); return; } m_ranges.append(range); compact(); } bool contains(const Range& range) const { if (range.begin() == range.end()) return false; unsigned index = findRange(range); if (index != UINT_MAX) return subsumesNonEmpty(m_ranges[index], range); return false; } bool overlaps(const Range& range) const { if (range.begin() == range.end()) return false; return findRange(range) != UINT_MAX; } void clear() { m_ranges.clear(); m_isCompact = true; } void dump(PrintStream& out) const { const_cast(this)->compact(); out.print(listDump(m_ranges)); } void dumpRaw(PrintStream& out) const { out.print("{", listDump(m_ranges), ", isCompact = ", m_isCompact, "}"); } typename VectorType::const_iterator begin() const { return m_ranges.begin(); } typename VectorType::const_iterator end() const { return m_ranges.end(); } void addAll(const RangeSet& other) { for (Range range : other) add(range); } void compact() { if (m_isCompact) return; if (m_ranges.isEmpty()) { m_isCompact = true; return; } std::sort( m_ranges.begin(), m_ranges.end(), [&] (const Range& a, const Range& b) -> bool { return a.begin() < b.begin(); }); unsigned srcIndex = 1; unsigned dstIndex = 1; Range* lastRange = &m_ranges[0]; while (srcIndex < m_ranges.size()) { Range range = m_ranges[srcIndex++]; ASSERT(range.begin() >= lastRange->begin()); if (range.end() <= lastRange->end()) continue; if (range.begin() <= lastRange->end()) { *lastRange = Range(lastRange->begin(), range.end()); continue; } ASSERT(!overlapsNonEmpty(*lastRange, range)); lastRange = &m_ranges[dstIndex++]; *lastRange = range; } m_ranges.shrink(dstIndex); m_isCompact = true; } private: static bool overlapsNonEmpty(const Range& a, const Range& b) { return nonEmptyRangesOverlap(a.begin(), a.end(), b.begin(), b.end()); } static bool subsumesNonEmpty(const Range& a, const Range& b) { return a.begin() <= b.begin() && a.end() >= b.end(); } unsigned findRange(const Range& range) const { const_cast(this)->compact(); // FIXME: Once we start using this in anger, we will want this to be a binary search. for (unsigned i = 0; i < m_ranges.size(); ++i) { if (overlapsNonEmpty(m_ranges[i], range)) return i; } return UINT_MAX; } VectorType m_ranges; bool m_isCompact { true }; }; } // namespace WTF using WTF::RangeSet;