blob: 09e3d6ef02805c7fb5783d2b60de1f6630811f72 [file] [log] [blame]
/*
* Copyright (C) 2011-2019 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
#if ENABLE(DFG_JIT)
#include "DFGRegisteredStructureSet.h"
#include "DFGTransition.h"
#include "DumpContext.h"
#include "JSCast.h"
#include "SpeculatedType.h"
#include "StructureSet.h"
namespace JSC {
class TrackedReferences;
namespace DFG {
class StructureAbstractValue {
public:
StructureAbstractValue() { }
StructureAbstractValue(RegisteredStructure structure)
: m_set(structure)
{
setClobbered(false);
}
StructureAbstractValue(const RegisteredStructureSet& other)
: m_set(other)
{
setClobbered(false);
}
ALWAYS_INLINE StructureAbstractValue(const StructureAbstractValue& other)
: m_set(other.m_set)
{
setClobbered(other.isClobbered());
}
ALWAYS_INLINE StructureAbstractValue& operator=(RegisteredStructure structure)
{
m_set = RegisteredStructureSet(structure);
setClobbered(false);
return *this;
}
ALWAYS_INLINE StructureAbstractValue& operator=(const RegisteredStructureSet& other)
{
m_set = other;
setClobbered(false);
return *this;
}
ALWAYS_INLINE StructureAbstractValue& operator=(const StructureAbstractValue& other)
{
m_set = other.m_set;
setClobbered(other.isClobbered());
return *this;
}
void clear()
{
m_set.clear();
setClobbered(false);
}
void makeTop()
{
m_set.deleteListIfNecessary();
m_set.m_pointer = topValue;
}
#if ASSERT_ENABLED
void assertIsRegistered(Graph&) const;
#else
void assertIsRegistered(Graph&) const { }
#endif
void clobber();
void observeInvalidationPoint() { setClobbered(false); }
void observeTransition(RegisteredStructure from, RegisteredStructure to);
void observeTransitions(const TransitionVector&);
static StructureAbstractValue top()
{
StructureAbstractValue result;
result.m_set.m_pointer = topValue;
return result;
}
bool isClear() const { return m_set.isEmpty(); }
bool isTop() const { return m_set.m_pointer == topValue; }
bool isNeitherClearNorTop() const { return !isClear() && !isTop(); }
// A clobbered abstract value means that the set currently contains the m_set set of
// structures plus TOP, except that the "plus TOP" will go away at the next invalidation
// point. Note that it's tempting to think of this as "the set of structures in m_set plus
// the set of structures transition-reachable from m_set" - but this isn't really correct,
// since if we add an unwatchable structure after clobbering, the two definitions are not
// equivalent. If we do this, the new unwatchable structure will be added to m_set.
// Invalidation points do not try to "clip" the set of transition-reachable structures from
// m_set by looking at reachability as this would mean that the new set is TOP. Instead they
// literally assume that the set is just m_set rather than m_set plus TOP.
bool isClobbered() const { return m_set.getReservedFlag(); }
// A finite structure abstract value is one where enumerating over it will yield all
// of the structures that the value may have right now. This is true so long as we're
// neither top nor clobbered.
bool isFinite() const { return !isTop() && !isClobbered(); }
// An infinite structure abstract value may currently have any structure.
bool isInfinite() const { return !isFinite(); }
bool add(RegisteredStructure);
bool merge(const RegisteredStructureSet& other);
ALWAYS_INLINE bool merge(const StructureAbstractValue& other)
{
if (other.isClear())
return false;
if (isTop())
return false;
if (other.isTop()) {
makeTop();
return true;
}
return mergeSlow(other);
}
void filter(const RegisteredStructureSet& other);
void filter(const StructureAbstractValue& other);
ALWAYS_INLINE void filter(SpeculatedType type)
{
if (!(type & SpecCell)) {
clear();
return;
}
if (isNeitherClearNorTop())
filterSlow(type);
}
ALWAYS_INLINE void filterClassInfo(const ClassInfo* classInfo)
{
if (isNeitherClearNorTop())
filterClassInfoSlow(classInfo);
}
ALWAYS_INLINE bool operator==(const StructureAbstractValue& other) const
{
if ((m_set.isThin() && other.m_set.isThin()) || isTop() || other.isTop())
return m_set.m_pointer == other.m_set.m_pointer;
return equalsSlow(other);
}
const RegisteredStructureSet& set() const
{
ASSERT(!isTop());
return m_set;
}
StructureSet toStructureSet() const
{
RELEASE_ASSERT(isFinite());
return m_set.toStructureSet();
}
size_t size() const
{
ASSERT(!isTop());
return m_set.size();
}
RegisteredStructure at(size_t i) const
{
ASSERT(!isTop());
return m_set.at(i);
}
RegisteredStructure operator[](size_t i) const { return at(i); }
// In most cases, what you really want to do is verify whether the set is top or clobbered, and
// if not, enumerate the set of structures. Use this only in cases where the singleton case is
// meaningfully special, like for transitions.
RegisteredStructure onlyStructure() const
{
if (isInfinite())
return RegisteredStructure();
return m_set.onlyStructure();
}
template<typename Functor>
void forEach(const Functor& functor) const
{
ASSERT(!isTop());
m_set.forEach(functor);
}
void dumpInContext(PrintStream&, DumpContext*) const;
void dump(PrintStream&) const;
// The methods below are all conservative and err on the side of making 'this' appear bigger
// than it is. For example, contains() may return true if the set is clobbered or TOP.
// isSubsetOf() may return false in case of ambiguities. Therefore you should only perform
// optimizations as a consequence of the "this is smaller" return value - so false for
// contains(), true for isSubsetOf(), false for isSupersetOf(), and false for overlaps().
bool contains(RegisteredStructure) const;
JS_EXPORT_PRIVATE bool contains(Structure* structure) const;
bool isSubsetOf(const RegisteredStructureSet& other) const;
bool isSubsetOf(const StructureAbstractValue& other) const;
bool isSupersetOf(const RegisteredStructureSet& other) const;
bool isSupersetOf(const StructureAbstractValue& other) const
{
return other.isSubsetOf(*this);
}
bool overlaps(const RegisteredStructureSet& other) const;
bool overlaps(const StructureAbstractValue& other) const;
bool isSubClassOf(const ClassInfo*) const;
bool isNotSubClassOf(const ClassInfo*) const;
void validateReferences(const TrackedReferences&) const;
private:
static constexpr uintptr_t clobberedFlag = RegisteredStructureSet::reservedFlag;
static constexpr uintptr_t topValue = RegisteredStructureSet::reservedValue;
static constexpr unsigned polymorphismLimit = 10;
static constexpr unsigned clobberedSupremacyThreshold = 2;
void filterSlow(SpeculatedType type);
void filterClassInfoSlow(const ClassInfo*);
bool mergeSlow(const StructureAbstractValue& other);
bool equalsSlow(const StructureAbstractValue& other) const;
void makeTopWhenThin()
{
ASSERT(m_set.isThin());
m_set.m_pointer = topValue;
}
bool mergeNotTop(const RegisteredStructureSet& other);
void setClobbered(bool clobbered)
{
ASSERT(!isTop() || !clobbered);
m_set.setReservedFlag(clobbered);
}
RegisteredStructureSet m_set;
};
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)