summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGAbstractValue.h
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGAbstractValue.h')
-rw-r--r--Source/JavaScriptCore/dfg/DFGAbstractValue.h531
1 files changed, 223 insertions, 308 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h
index 25757b54e..480842860 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-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,17 +26,29 @@
#ifndef DFGAbstractValue_h
#define DFGAbstractValue_h
-#include <wtf/Platform.h>
-
#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()
@@ -49,50 +61,111 @@ struct AbstractValue {
{
m_type = SpecNone;
m_arrayModes = 0;
- m_currentKnownStructure.clear();
- m_futurePossibleStructure.clear();
+ m_structure.clear();
m_value = JSValue();
checkConsistency();
}
- bool isClear() const
+ bool isClear() const { return m_type == SpecNone; }
+ bool operator!() const { return isClear(); }
+
+ void makeHeapTop()
{
- bool result = m_type == SpecNone && !m_arrayModes && m_currentKnownStructure.isClear() && m_futurePossibleStructure.isClear();
- if (result)
- ASSERT(!m_value);
- return result;
+ makeTop(SpecHeapTop);
}
- void makeTop()
+ void makeBytecodeTop()
{
- m_type |= SpecTop; // The state may have included SpecEmpty, in which case we want this to become SpecEmptyOrTop.
- m_arrayModes = ALL_ARRAY_MODES;
- m_currentKnownStructure.makeTop();
- m_futurePossibleStructure.makeTop();
- m_value = JSValue();
- checkConsistency();
+ 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(Structure* from, Structure* 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(Structure* from, Structure* to)
+ : m_from(from)
+ , m_to(to)
+ {
+ }
+ void operator()(AbstractValue& value)
+ {
+ value.observeTransition(m_from, m_to);
+ }
+ private:
+ Structure* m_from;
+ Structure* 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();
}
- bool isTop() const
+ bool isHeapTop() const
{
- return m_type == SpecTop && 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
@@ -105,85 +178,56 @@ struct AbstractValue {
return m_value;
}
- static AbstractValue top()
+ static AbstractValue heapTop()
{
AbstractValue result;
- result.makeTop();
+ result.makeHeapTop();
return result;
}
- void setMostSpecific(JSValue value)
+ static AbstractValue bytecodeTop()
{
- if (!!value && value.isCell()) {
- Structure* structure = value.asCell()->structure();
- m_currentKnownStructure = structure;
- setFuturePossibleStructure(structure);
- m_arrayModes = asArrayModes(structure->indexingType());
- } else {
- m_currentKnownStructure.clear();
- m_futurePossibleStructure.clear();
- m_arrayModes = 0;
- }
-
- m_type = speculationFromValue(value);
- m_value = value;
-
- checkConsistency();
+ AbstractValue result;
+ result.makeBytecodeTop();
+ return result;
}
- void set(JSValue value)
+ static AbstractValue fullTop()
{
- if (!!value && value.isCell()) {
- m_currentKnownStructure.makeTop();
- Structure* structure = value.asCell()->structure();
- setFuturePossibleStructure(structure);
- m_arrayModes = asArrayModes(structure->indexingType());
- clobberArrayModes();
- } else {
- m_currentKnownStructure.clear();
- m_futurePossibleStructure.clear();
- m_arrayModes = 0;
- }
-
- m_type = speculationFromValue(value);
- m_value = value;
-
- checkConsistency();
+ AbstractValue result;
+ result.makeFullTop();
+ return result;
}
- void set(Structure* structure)
- {
- m_currentKnownStructure = structure;
- setFuturePossibleStructure(structure);
- m_arrayModes = asArrayModes(structure->indexingType());
- m_type = speculationFromStructure(structure);
- m_value = JSValue();
-
- checkConsistency();
- }
+ void set(Graph&, const FrozenValue&, StructureClobberState);
+ void set(Graph&, Structure*);
+ void set(Graph&, const StructureSet&);
+
+ // Set this value to represent the given set of types as precisely as possible.
+ void setType(Graph&, SpeculatedType);
- void set(SpeculatedType type)
+ // 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
@@ -206,8 +250,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();
@@ -218,13 +261,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();
@@ -232,90 +276,39 @@ struct AbstractValue {
checkConsistency();
}
- void filter(const StructureSet& other)
+ bool couldBeType(SpeculatedType desiredType) const
{
- // FIXME: This could be optimized for the common case of m_type not
- // having structures, array modes, or a specific value.
- // https://bugs.webkit.org/show_bug.cgi?id=109663
- m_type &= other.speculationFromStructures();
- m_arrayModes &= other.arrayModesFromStructures();
- m_currentKnownStructure.filter(other);
- if (m_currentKnownStructure.isClear())
- m_futurePossibleStructure.clear();
- else if (m_currentKnownStructure.hasSingleton())
- filterFuturePossibleStructure(m_currentKnownStructure.singleton());
-
- // It's possible that prior to the above two statements we had (Foo, TOP), where
- // Foo is a SpeculatedType that is disjoint with the passed StructureSet. In that
- // case, we will now have (None, [someStructure]). In general, we need to make
- // sure that new information gleaned from the SpeculatedType needs to be fed back
- // into the information gleaned from the StructureSet.
- m_currentKnownStructure.filter(m_type);
- m_futurePossibleStructure.filter(m_type);
-
- filterArrayModesByType();
- filterValueByType();
-
- checkConsistency();
- }
-
- void filterArrayModes(ArrayModes arrayModes)
- {
- ASSERT(arrayModes);
-
- m_type &= SpecCell;
- m_arrayModes &= arrayModes;
-
- // I could do more fancy filtering here. But it probably won't make any difference.
-
- checkConsistency();
- }
-
- void filter(SpeculatedType type)
- {
- if (type == SpecTop)
- return;
- m_type &= type;
-
- // It's possible that prior to this filter() call we had, say, (Final, TOP), and
- // the passed type is Array. At this point we'll have (None, TOP). The best way
- // to ensure that the structure filtering does the right thing is to filter on
- // the new type (None) rather than the one passed (Array).
- m_currentKnownStructure.filter(m_type);
- m_futurePossibleStructure.filter(m_type);
-
- filterArrayModesByType();
- filterValueByType();
-
- checkConsistency();
+ return !!(m_type & desiredType);
}
- void filterByValue(JSValue value)
+ bool isType(SpeculatedType desiredType) const
{
- filter(speculationFromValue(value));
- if (m_type)
- m_value = value;
+ 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 StructureSet&, SpeculatedType admittedTypes = SpecNone);
+
+ FiltrationResult filterArrayModes(ArrayModes);
+ FiltrationResult filter(SpeculatedType);
+ FiltrationResult filterByValue(const FrozenValue& value);
+ FiltrationResult filter(const AbstractValue&);
+
+ FiltrationResult filter(Graph&, const InferredType::Descriptor&);
- bool validateType(JSValue value) const
- {
- if (isTop())
- return true;
-
- if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type)
- return false;
-
- if (value.isEmpty()) {
- ASSERT(m_type & SpecEmpty);
- return true;
- }
-
- return true;
- }
+ FiltrationResult changeStructure(Graph&, const StructureSet&);
+ bool contains(Structure*) const;
+
bool validate(JSValue value) const
{
- if (isTop())
+ if (isHeapTop())
return true;
if (!!m_value && m_value != value)
@@ -332,90 +325,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
+ bool hasClobberableState() const
{
- if (m_currentKnownStructure.hasSingleton())
- return m_currentKnownStructure.singleton();
- if (m_futurePossibleStructure.hasSingleton())
- return m_futurePossibleStructure.singleton();
- return 0;
+ return m_structure.isNeitherClearNorTop()
+ || !arrayModesAreClearOrTop(m_arrayModes);
}
- void checkConsistency() const
- {
- if (!(m_type & SpecCell)) {
- ASSERT(m_currentKnownStructure.isClear());
- ASSERT(m_futurePossibleStructure.isClear());
- ASSERT(!m_arrayModes);
- }
-
- if (isClear())
- ASSERT(!m_value);
-
- if (!!m_value)
- ASSERT(mergeSpeculations(m_type, speculationFromValue(m_value)) == m_type);
-
- // Note that it's possible for a prediction like (Final, []). This really means that
- // the value is bottom and that any code that uses the value is unreachable. But
- // we don't want to get pedantic about this as it would only increase the computational
- // complexity of the code.
- }
+#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;
- void dump(PrintStream& out) const
- {
- out.print(
- "(", SpeculationDump(m_type), ", ", ArrayModesDump(m_arrayModes), ", ",
- m_currentKnownStructure, ", ", m_futurePossibleStructure);
- if (!!m_value)
- out.print(", ", m_value);
- out.print(")");
- }
+ void validateReferences(const TrackedReferences&);
- // 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 currently 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.
-
// 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
@@ -423,44 +360,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|SpecInt32 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
@@ -475,7 +393,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:
@@ -486,57 +408,50 @@ private:
m_arrayModes = ALL_ARRAY_MODES;
}
- void setFuturePossibleStructure(Structure* structure)
+ void observeIndexingTypeTransition(IndexingType from, IndexingType to)
{
- if (structure->transitionWatchpointSetIsStillValid())
- m_futurePossibleStructure = structure;
- else
- m_futurePossibleStructure.makeTop();
+ if (m_arrayModes & asArrayModes(from))
+ m_arrayModes |= asArrayModes(to);
}
- void filterFuturePossibleStructure(Structure* structure)
- {
- if (structure->transitionWatchpointSetIsStillValid())
- m_futurePossibleStructure.filter(StructureAbstractValue(structure));
- }
-
- // We could go further, and ensure that if the futurePossibleStructure contravenes
- // the value, then we could clear both of those things. But that's unlikely to help
- // in any realistic scenario, so we don't do it. Simpler is better.
- void filterValueByType()
+ bool validateType(JSValue value) const
{
- if (!!m_type) {
- // The type is still non-empty. This implies that regardless of what filtering
- // was done, we either didn't have a value to begin with, or that value is still
- // valid.
- ASSERT(!m_value || validateType(m_value));
- return;
+ 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,
+ // and that's fine - the type validates just fine.
+ SpeculatedType type = m_type;
+ if (type & SpecInt52)
+ type |= SpecInt52AsDouble;
+
+ if (mergeSpeculations(type, speculationFromValue(value)) != type)
+ return false;
+
+ if (value.isEmpty()) {
+ ASSERT(m_type & SpecEmpty);
+ return true;
}
- // The type has been rendered empty. That means that the value must now be invalid,
- // as well.
- ASSERT(!m_value || !validateType(m_value));
- m_value = JSValue();
+ return true;
}
- void filterArrayModesByType()
+ void makeTop(SpeculatedType top)
{
- if (!(m_type & SpecCell))
- m_arrayModes = 0;
- else if (!(m_type & ~SpecArray))
- m_arrayModes &= ALL_ARRAY_ARRAY_MODES;
-
- // NOTE: If m_type doesn't have SpecArray set, that doesn't mean that the
- // array modes have to be a subset of ALL_NON_ARRAY_ARRAY_MODES, since
- // in the speculated type type-system, RegExpMatchesArry and ArrayPrototype
- // are Otherobj (since they are not *exactly* JSArray) but in the ArrayModes
- // type system they are arrays (since they expose the magical length
- // property and are otherwise allocated using array allocation). Hence the
- // following would be wrong:
- //
- // if (!(m_type & SpecArray))
- // m_arrayModes &= ALL_NON_ARRAY_ARRAY_MODES;
+ m_type |= top;
+ m_arrayModes = ALL_ARRAY_MODES;
+ m_structure.makeTop();
+ m_value = JSValue();
+ checkConsistency();
}
+
+ void filterValueByType();
+ void filterArrayModesByType();
+
+ bool shouldBeClear() const;
+ FiltrationResult normalizeClarity();
+ FiltrationResult normalizeClarity(Graph&);
};
} } // namespace JSC::DFG