123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- /*
- * Copyright (C) 2011, 2012, 2013 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.
- */
- #ifndef DFGAbstractValue_h
- #define DFGAbstractValue_h
- #include <wtf/Platform.h>
- #if ENABLE(DFG_JIT)
- #include "ArrayProfile.h"
- #include "DFGStructureAbstractValue.h"
- #include "JSCell.h"
- #include "SpeculatedType.h"
- #include "StructureSet.h"
- namespace JSC { namespace DFG {
- struct AbstractValue {
- AbstractValue()
- : m_type(SpecNone)
- , m_arrayModes(0)
- {
- }
-
- void clear()
- {
- m_type = SpecNone;
- m_arrayModes = 0;
- m_currentKnownStructure.clear();
- m_futurePossibleStructure.clear();
- m_value = JSValue();
- checkConsistency();
- }
-
- bool isClear() const
- {
- bool result = m_type == SpecNone && !m_arrayModes && m_currentKnownStructure.isClear() && m_futurePossibleStructure.isClear();
- if (result)
- ASSERT(!m_value);
- return result;
- }
-
- void makeTop()
- {
- 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();
- }
-
- void clobberStructures()
- {
- if (m_type & SpecCell) {
- m_currentKnownStructure.makeTop();
- clobberArrayModes();
- } else {
- ASSERT(m_currentKnownStructure.isClear());
- ASSERT(!m_arrayModes);
- }
- checkConsistency();
- }
-
- void clobberValue()
- {
- m_value = JSValue();
- }
-
- bool isTop() const
- {
- return m_type == SpecTop && m_currentKnownStructure.isTop() && m_futurePossibleStructure.isTop();
- }
-
- bool valueIsTop() const
- {
- return !m_value && m_type;
- }
-
- JSValue value() const
- {
- return m_value;
- }
-
- static AbstractValue top()
- {
- AbstractValue result;
- result.makeTop();
- return result;
- }
-
- void setMostSpecific(JSValue value)
- {
- 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();
- }
-
- void set(JSValue value)
- {
- 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();
- }
-
- void set(Structure* structure)
- {
- m_currentKnownStructure = structure;
- setFuturePossibleStructure(structure);
- m_arrayModes = asArrayModes(structure->indexingType());
- m_type = speculationFromStructure(structure);
- m_value = JSValue();
-
- checkConsistency();
- }
-
- void set(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;
- }
- m_type = type;
- m_value = JSValue();
- checkConsistency();
- }
-
- 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_value == other.m_value;
- }
- bool operator!=(const AbstractValue& other) const
- {
- return !(*this == other);
- }
-
- bool merge(const AbstractValue& other)
- {
- if (other.isClear())
- return false;
-
- #if !ASSERT_DISABLED
- AbstractValue oldMe = *this;
- #endif
- bool result = false;
- if (isClear()) {
- *this = other;
- result = !other.isClear();
- } 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);
- if (m_value != other.m_value) {
- result |= !!m_value;
- m_value = JSValue();
- }
- }
- checkConsistency();
- ASSERT(result == (*this != oldMe));
- return result;
- }
-
- void merge(SpeculatedType type)
- {
- mergeSpeculation(m_type, type);
-
- if (type & SpecCell) {
- m_currentKnownStructure.makeTop();
- m_futurePossibleStructure.makeTop();
- m_arrayModes = ALL_ARRAY_MODES;
- }
- m_value = JSValue();
- checkConsistency();
- }
-
- void filter(const StructureSet& other)
- {
- // 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();
- }
-
- void filterByValue(JSValue value)
- {
- filter(speculationFromValue(value));
- if (m_type)
- m_value = value;
- }
-
- 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;
- }
-
- bool validate(JSValue value) const
- {
- if (isTop())
- return true;
-
- if (!!m_value && m_value != value)
- return false;
-
- if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type)
- return false;
-
- if (value.isEmpty()) {
- ASSERT(m_type & SpecEmpty);
- return true;
- }
-
- if (!!value && value.isCell()) {
- ASSERT(m_type & SpecCell);
- Structure* structure = value.asCell()->structure();
- return m_currentKnownStructure.contains(structure)
- && m_futurePossibleStructure.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;
- }
-
- 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.
- }
-
- 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(")");
- }
-
- // 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
- // case the current value can have any structure. The set may be BOTTOM (empty)
- // 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;
-
- // 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.
- SpeculatedType m_type;
-
- // This is a proven constraint on the possible indexing types that this value
- // can have right now. It also implicitly constraints the set of structures
- // that the value may have right now, since a structure has an immutable
- // indexing type. This is subject to change upon reassignment, or any side
- // effect that makes non-obvious changes to the heap.
- ArrayModes m_arrayModes;
-
- // This is a proven constraint on the possible values that this value can
- // have now or any time in the future, unless it is reassigned. Note that this
- // 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.
- JSValue m_value;
- private:
- void clobberArrayModes()
- {
- // FIXME: We could make this try to predict the set of array modes that this object
- // could have in the future. For now, just do the simple thing.
- m_arrayModes = ALL_ARRAY_MODES;
- }
-
- void setFuturePossibleStructure(Structure* structure)
- {
- if (structure->transitionWatchpointSetIsStillValid())
- m_futurePossibleStructure = structure;
- else
- m_futurePossibleStructure.makeTop();
- }
-
- 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()
- {
- 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;
- }
-
- // 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();
- }
-
- void filterArrayModesByType()
- {
- 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;
- }
- };
- } } // namespace JSC::DFG
- #endif // ENABLE(DFG_JIT)
- #endif // DFGAbstractValue_h
|