diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGAbstractValue.h')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGAbstractValue.h | 344 |
1 files changed, 209 insertions, 135 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h index db313d242..03a505687 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractValue.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011-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 @@ -23,38 +23,52 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DFGAbstractValue_h -#define DFGAbstractValue_h - -#include <wtf/Platform.h> +#pragma once #if ENABLE(DFG_JIT) #include "ArrayProfile.h" #include "DFGFiltrationResult.h" +#include "DFGFrozenValue.h" +#include "DFGNodeFlags.h" #include "DFGStructureAbstractValue.h" +#include "DFGStructureClobberState.h" +#include "InferredType.h" #include "JSCell.h" +#include "ResultType.h" #include "SpeculatedType.h" #include "DumpContext.h" -#include "StructureSet.h" -namespace JSC { namespace DFG { +namespace JSC { + +class TrackedReferences; + +namespace DFG { class Graph; +struct Node; struct AbstractValue { AbstractValue() : m_type(SpecNone) , m_arrayModes(0) { +#if USE(JSVALUE64) && !defined(NDEBUG) + // The WTF Traits for AbstractValue allow the initialization of values with bzero(). + // We verify the correctness of this assumption here. + static bool needsDefaultConstructorCheck = true; + if (needsDefaultConstructorCheck) { + needsDefaultConstructorCheck = false; + ensureCanInitializeWithZeros(); + } +#endif } void clear() { m_type = SpecNone; m_arrayModes = 0; - m_currentKnownStructure.clear(); - m_futurePossibleStructure.clear(); + m_structure.clear(); m_value = JSValue(); checkConsistency(); } @@ -72,18 +86,82 @@ struct AbstractValue { makeTop(SpecBytecodeTop); } + void makeFullTop() + { + makeTop(SpecFullTop); + } + void clobberStructures() { if (m_type & SpecCell) { - m_currentKnownStructure.makeTop(); + m_structure.clobber(); clobberArrayModes(); } else { - ASSERT(m_currentKnownStructure.isClear()); + ASSERT(m_structure.isClear()); ASSERT(!m_arrayModes); } checkConsistency(); } + + static void clobberStructuresFor(AbstractValue& value) + { + value.clobberStructures(); + } + + void observeInvalidationPoint() + { + m_structure.observeInvalidationPoint(); + checkConsistency(); + } + + static void observeInvalidationPointFor(AbstractValue& value) + { + value.observeInvalidationPoint(); + } + + void observeTransition(RegisteredStructure from, RegisteredStructure to) + { + if (m_type & SpecCell) { + m_structure.observeTransition(from, to); + observeIndexingTypeTransition(from->indexingType(), to->indexingType()); + } + checkConsistency(); + } + + void observeTransitions(const TransitionVector& vector); + + class TransitionObserver { + public: + TransitionObserver(RegisteredStructure from, RegisteredStructure to) + : m_from(from) + , m_to(to) + { + } + + void operator()(AbstractValue& value) + { + value.observeTransition(m_from, m_to); + } + private: + RegisteredStructure m_from; + RegisteredStructure m_to; + }; + + class TransitionsObserver { + public: + TransitionsObserver(const TransitionVector& vector) + : m_vector(vector) + { + } + void operator()(AbstractValue& value) + { + value.observeTransitions(m_vector); + } + private: + const TransitionVector& m_vector; + }; + void clobberValue() { m_value = JSValue(); @@ -91,7 +169,10 @@ struct AbstractValue { bool isHeapTop() const { - return (m_type | SpecHeapTop) == m_type && m_currentKnownStructure.isTop() && m_futurePossibleStructure.isTop(); + return (m_type | SpecHeapTop) == m_type + && m_structure.isTop() + && m_arrayModes == ALL_ARRAY_MODES + && !m_value; } bool valueIsTop() const @@ -111,32 +192,50 @@ struct AbstractValue { return result; } - void setMostSpecific(Graph&, JSValue); - void set(Graph&, JSValue); + static AbstractValue bytecodeTop() + { + AbstractValue result; + result.makeBytecodeTop(); + return result; + } + + static AbstractValue fullTop() + { + AbstractValue result; + result.makeFullTop(); + return result; + } + + void set(Graph&, const FrozenValue&, StructureClobberState); void set(Graph&, Structure*); + void set(Graph&, RegisteredStructure); + void set(Graph&, const RegisteredStructureSet&); + // Set this value to represent the given set of types as precisely as possible. + void setType(Graph&, SpeculatedType); + + // As above, but only valid for non-cell types. void setType(SpeculatedType type) { - if (type & SpecCell) { - m_currentKnownStructure.makeTop(); - m_futurePossibleStructure.makeTop(); - m_arrayModes = ALL_ARRAY_MODES; - } else { - m_currentKnownStructure.clear(); - m_futurePossibleStructure.clear(); - m_arrayModes = 0; - } + RELEASE_ASSERT(!(type & SpecCell)); + m_structure.clear(); + m_arrayModes = 0; m_type = type; m_value = JSValue(); checkConsistency(); } + + void set(Graph&, const InferredType::Descriptor&); + void set(Graph&, const InferredType::Descriptor&, StructureClobberState); + + void fixTypeForRepresentation(Graph&, NodeFlags representation, Node* = nullptr); + void fixTypeForRepresentation(Graph&, Node*); bool operator==(const AbstractValue& other) const { return m_type == other.m_type && m_arrayModes == other.m_arrayModes - && m_currentKnownStructure == other.m_currentKnownStructure - && m_futurePossibleStructure == other.m_futurePossibleStructure + && m_structure == other.m_structure && m_value == other.m_value; } bool operator!=(const AbstractValue& other) const @@ -159,8 +258,7 @@ struct AbstractValue { } else { result |= mergeSpeculation(m_type, other.m_type); result |= mergeArrayModes(m_arrayModes, other.m_arrayModes); - result |= m_currentKnownStructure.addAll(other.m_currentKnownStructure); - result |= m_futurePossibleStructure.addAll(other.m_futurePossibleStructure); + result |= m_structure.merge(other.m_structure); if (m_value != other.m_value) { result |= !!m_value; m_value = JSValue(); @@ -171,13 +269,14 @@ struct AbstractValue { return result; } + bool mergeOSREntryValue(Graph&, JSValue); + void merge(SpeculatedType type) { mergeSpeculation(m_type, type); if (type & SpecCell) { - m_currentKnownStructure.makeTop(); - m_futurePossibleStructure.makeTop(); + m_structure.makeTop(); m_arrayModes = ALL_ARRAY_MODES; } m_value = JSValue(); @@ -185,24 +284,37 @@ struct AbstractValue { checkConsistency(); } - bool couldBeType(SpeculatedType desiredType) + bool couldBeType(SpeculatedType desiredType) const { return !!(m_type & desiredType); } - bool isType(SpeculatedType desiredType) + bool isType(SpeculatedType desiredType) const { return !(m_type & ~desiredType); } + + bool isType(Graph&, const InferredType::Descriptor&) const; + + // Filters the value using the given structure set. If the admittedTypes argument is not passed, this + // implicitly filters by the types implied by the structure set, which are usually a subset of + // SpecCell. Hence, after this call, the value will no longer have any non-cell members. But, you can + // use admittedTypes to preserve some non-cell types. Note that it's wrong for admittedTypes to overlap + // with SpecCell. + FiltrationResult filter(Graph&, const RegisteredStructureSet&, SpeculatedType admittedTypes = SpecNone); + + FiltrationResult filterArrayModes(ArrayModes); + FiltrationResult filter(SpeculatedType); + FiltrationResult filterByValue(const FrozenValue& value); + FiltrationResult filter(const AbstractValue&); + FiltrationResult filterClassInfo(Graph&, const ClassInfo*); + + FiltrationResult filter(Graph&, const InferredType::Descriptor&); - FiltrationResult filter(Graph&, const StructureSet&); - - FiltrationResult filterArrayModes(ArrayModes arrayModes); - - FiltrationResult filter(SpeculatedType type); - - FiltrationResult filterByValue(JSValue value); + FiltrationResult changeStructure(Graph&, const RegisteredStructureSet&); + bool contains(RegisteredStructure) const; + bool validate(JSValue value) const { if (isHeapTop()) @@ -222,75 +334,34 @@ struct AbstractValue { if (!!value && value.isCell()) { ASSERT(m_type & SpecCell); Structure* structure = value.asCell()->structure(); - return m_currentKnownStructure.contains(structure) - && m_futurePossibleStructure.contains(structure) + return m_structure.contains(structure) && (m_arrayModes & asArrayModes(structure->indexingType())); } return true; } - Structure* bestProvenStructure() const - { - if (m_currentKnownStructure.hasSingleton()) - return m_currentKnownStructure.singleton(); - if (m_futurePossibleStructure.hasSingleton()) - return m_futurePossibleStructure.singleton(); - return 0; - } - bool hasClobberableState() const { - return m_currentKnownStructure.isNeitherClearNorTop() + return m_structure.isNeitherClearNorTop() || !arrayModesAreClearOrTop(m_arrayModes); } #if ASSERT_DISABLED void checkConsistency() const { } + void assertIsRegistered(Graph&) const { } #else void checkConsistency() const; + void assertIsRegistered(Graph&) const; #endif - + + ResultType resultType() const; + void dumpInContext(PrintStream&, DumpContext*) const; void dump(PrintStream&) const; - // A great way to think about the difference between m_currentKnownStructure and - // m_futurePossibleStructure is to consider these four examples: - // - // 1) x = foo(); - // - // In this case x's m_currentKnownStructure and m_futurePossibleStructure will - // both be TOP, since we don't know anything about x for sure, yet. - // - // 2) x = foo(); - // y = x.f; - // - // Where x will later have a new property added to it, 'g'. Because of the - // known but not-yet-executed property addition, x's current structure will - // not be watchpointable; hence we have no way of statically bounding the set - // of possible structures that x may have if a clobbering event happens. So, - // x's m_currentKnownStructure will be whatever structure we check to get - // property 'f', and m_futurePossibleStructure will be TOP. - // - // 3) x = foo(); - // y = x.f; - // - // Where x has a terminal structure that is still watchpointable. In this case, - // x's m_currentKnownStructure and m_futurePossibleStructure will both be - // whatever structure we checked for when getting 'f'. - // - // 4) x = foo(); - // y = x.f; - // bar(); - // - // Where x has a terminal structure that is still watchpointable. In this - // case, m_currentKnownStructure will be TOP because bar() may potentially - // change x's structure and we have no way of proving otherwise, but - // x's m_futurePossibleStructure will be whatever structure we had checked - // when getting property 'f'. - - // NB. All fields in this struct must have trivial destructors. - + void validateReferences(const TrackedReferences&); + // This is a proven constraint on the structures that this value can have right // now. The structure of the current value must belong to this set. The set may // be TOP, indicating that it is the set of all possible structures, in which @@ -298,44 +369,25 @@ struct AbstractValue { // in which case this value cannot be a cell. This is all subject to change // anytime a new value is assigned to this one, anytime there is a control flow // merge, or most crucially, anytime a side-effect or structure check happens. - // In case of a side-effect, we typically must assume that any value may have - // had its structure changed, hence contravening our proof. We make the proof - // valid again by switching this to TOP (i.e. claiming that we have proved that - // this value may have any structure). Of note is that the proof represented by - // this field is not subject to structure transition watchpoints - even if one - // fires, we can be sure that this proof is still valid. - StructureAbstractValue m_currentKnownStructure; - - // This is a proven constraint on the structures that this value can have now - // or any time in the future subject to the structure transition watchpoints of - // all members of this set not having fired. This set is impervious to side- - // effects; even if one happens the side-effect can only cause the value to - // change to at worst another structure that is also a member of this set. But, - // the theorem being proved by this field is predicated upon there not being - // any new structure transitions introduced into any members of this set. In - // cases where there is no way for us to guard this happening, the set must be - // TOP. But in cases where we can guard new structure transitions (all members - // of the set have still-valid structure transition watchpoints) then this set - // will be finite. Anytime that we make use of the finite nature of this set, - // we must first issue a structure transition watchpoint, which will effectively - // result in m_currentKnownStructure being filtered according to - // m_futurePossibleStructure. - StructureAbstractValue m_futurePossibleStructure; + // In case of a side-effect, we must assume that any value with a structure that + // isn't being watched may have had its structure changed, hence contravening + // our proof. In such a case we make the proof valid again by switching this to + // TOP (i.e. claiming that we have proved that this value may have any + // structure). + StructureAbstractValue m_structure; // This is a proven constraint on the possible types that this value can have // now or any time in the future, unless it is reassigned. This field is - // impervious to side-effects unless the side-effect can reassign the value - // (for example if we're talking about a captured variable). The relationship - // between this field, and the structure fields above, is as follows. The - // fields above constraint the structures that a cell may have, but they say - // nothing about whether or not the value is known to be a cell. More formally, - // the m_currentKnownStructure is itself an abstract value that consists of the - // union of the set of all non-cell values and the set of cell values that have - // the given structure. This abstract value is then the intersection of the - // m_currentKnownStructure and the set of values whose type is m_type. So, for - // example if m_type is SpecFinal|SpecInt32 and m_currentKnownStructure is - // [0x12345] then this abstract value corresponds to the set of all integers - // unified with the set of all objects with structure 0x12345. + // impervious to side-effects. The relationship between this field, and the + // structure fields above, is as follows. The fields above constraint the + // structures that a cell may have, but they say nothing about whether or not + // the value is known to be a cell. More formally, the m_structure is itself an + // abstract value that consists of the union of the set of all non-cell values + // and the set of cell values that have the given structure. This abstract + // value is then the intersection of the m_structure and the set of values + // whose type is m_type. So, for example if m_type is SpecFinal|SpecInt32Only and + // m_structure is [0x12345] then this abstract value corresponds to the set of + // all integers unified with the set of all objects with structure 0x12345. SpeculatedType m_type; // This is a proven constraint on the possible indexing types that this value @@ -350,7 +402,11 @@ struct AbstractValue { // implies nothing about the structure. Oddly, JSValue() (i.e. the empty value) // means either BOTTOM or TOP depending on the state of m_type: if m_type is // BOTTOM then JSValue() means BOTTOM; if m_type is not BOTTOM then JSValue() - // means TOP. + // means TOP. Also note that this value isn't necessarily known to the GC + // (strongly or even weakly - it may be an "fragile" value, see + // DFGValueStrength.h). If you perform any optimization based on a cell m_value + // that requires that the value be kept alive, you must call freeze() on that + // value, which will turn it into a weak value. JSValue m_value; private: @@ -361,17 +417,23 @@ private: m_arrayModes = ALL_ARRAY_MODES; } + void observeIndexingTypeTransition(IndexingType from, IndexingType to) + { + if (m_arrayModes & asArrayModes(from)) + m_arrayModes |= asArrayModes(to); + } + bool validateType(JSValue value) const { if (isHeapTop()) return true; - // Constant folding always represents Int52's in a double (i.e. Int52AsDouble). - // So speculationFromValue(value) for an Int52 value will return Int52AsDouble, + // Constant folding always represents Int52's in a double (i.e. AnyIntAsDouble). + // So speculationFromValue(value) for an Int52 value will return AnyIntAsDouble, // and that's fine - the type validates just fine. SpeculatedType type = m_type; - if (type & SpecInt52) - type |= SpecInt52AsDouble; + if (type & SpecInt52Only) + type |= SpecAnyIntAsDouble; if (mergeSpeculations(type, speculationFromValue(value)) != type) return false; @@ -388,25 +450,37 @@ private: { m_type |= top; m_arrayModes = ALL_ARRAY_MODES; - m_currentKnownStructure.makeTop(); - m_futurePossibleStructure.makeTop(); + m_structure.makeTop(); m_value = JSValue(); checkConsistency(); } - void setFuturePossibleStructure(Graph&, Structure* structure); - void filterValueByType(); void filterArrayModesByType(); + +#if USE(JSVALUE64) && !defined(NDEBUG) + void ensureCanInitializeWithZeros(); +#endif bool shouldBeClear() const; FiltrationResult normalizeClarity(); + FiltrationResult normalizeClarity(Graph&); }; } } // namespace JSC::DFG -#endif // ENABLE(DFG_JIT) - -#endif // DFGAbstractValue_h +#if USE(JSVALUE64) +namespace WTF { +template <> +struct VectorTraits<JSC::DFG::AbstractValue> : VectorTraitsBase<false, JSC::DFG::AbstractValue> { + static const bool canInitializeWithMemset = true; +}; +template <> +struct HashTraits<JSC::DFG::AbstractValue> : GenericHashTraits<JSC::DFG::AbstractValue> { + static const bool emptyValueIsZero = true; +}; +}; +#endif // USE(JSVALUE64) +#endif // ENABLE(DFG_JIT) |