/* * Copyright (C) 2011 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 #if ENABLE(DFG_JIT) #include "JSCell.h" #include "PredictedType.h" #include "StructureSet.h" namespace JSC { namespace DFG { class StructureAbstractValue { public: StructureAbstractValue() : m_structure(0) { } StructureAbstractValue(Structure* structure) : m_structure(structure) { } StructureAbstractValue(const StructureSet& set) { switch (set.size()) { case 0: m_structure = 0; break; case 1: m_structure = set[0]; break; default: m_structure = topValue(); break; } } void clear() { m_structure = 0; } void makeTop() { m_structure = topValue(); } static StructureAbstractValue top() { StructureAbstractValue value; value.makeTop(); return value; } void add(Structure* structure) { ASSERT(!contains(structure) && !isTop()); if (m_structure) makeTop(); else m_structure = structure; } bool addAll(const StructureSet& other) { if (isTop() || !other.size()) return false; if (other.size() > 1) { makeTop(); return true; } if (!m_structure) { m_structure = other[0]; return true; } if (m_structure == other[0]) return false; makeTop(); return true; } bool addAll(const StructureAbstractValue& other) { if (!other.m_structure) return false; if (isTop()) return false; if (other.isTop()) { makeTop(); return true; } if (m_structure) { if (m_structure == other.m_structure) return false; makeTop(); return true; } m_structure = other.m_structure; return true; } bool contains(Structure* structure) const { if (isTop()) return true; if (m_structure == structure) return true; return false; } bool isSubsetOf(const StructureSet& other) const { if (isTop()) return false; if (!m_structure) return true; return other.contains(m_structure); } bool doesNotContainAnyOtherThan(Structure* structure) const { if (isTop()) return false; if (!m_structure) return true; return m_structure == structure; } bool isSupersetOf(const StructureSet& other) const { if (isTop()) return true; if (!other.size()) return true; if (other.size() > 1) return false; return m_structure == other[0]; } bool isSubsetOf(const StructureAbstractValue& other) const { if (other.isTop()) return true; if (isTop()) return false; if (m_structure) { if (other.m_structure) return m_structure == other.m_structure; return false; } return true; } bool isSupersetOf(const StructureAbstractValue& other) const { return other.isSubsetOf(*this); } void filter(const StructureSet& other) { if (!m_structure) return; if (isTop()) { switch (other.size()) { case 0: m_structure = 0; return; case 1: m_structure = other[0]; return; default: return; } } if (other.contains(m_structure)) return; m_structure = 0; } void filter(const StructureAbstractValue& other) { if (isTop()) { m_structure = other.m_structure; return; } if (m_structure == other.m_structure) return; if (other.isTop()) return; m_structure = 0; } void filter(PredictedType other) { if (!(other & PredictCell)) { clear(); return; } if (isClearOrTop()) return; if (!(predictionFromStructure(m_structure) & other)) m_structure = 0; } bool isClear() const { return !m_structure; } bool isTop() const { return m_structure == topValue(); } bool isClearOrTop() const { return m_structure <= topValue(); } bool isNeitherClearNorTop() const { return !isClearOrTop(); } size_t size() const { ASSERT(!isTop()); return !!m_structure; } Structure* at(size_t i) const { ASSERT(!isTop()); ASSERT(m_structure); ASSERT_UNUSED(i, !i); return m_structure; } Structure* operator[](size_t i) const { return at(i); } Structure* last() const { return at(0); } PredictedType predictionFromStructures() const { if (isTop()) return PredictCell; if (isClear()) return PredictNone; return predictionFromStructure(m_structure); } bool operator==(const StructureAbstractValue& other) const { return m_structure == other.m_structure; } void dump(FILE* out) const { if (isTop()) { fprintf(out, "TOP"); return; } fprintf(out, "["); if (m_structure) fprintf(out, "%p", m_structure); fprintf(out, "]"); } private: static Structure* topValue() { return reinterpret_cast(1); } // This can only remember one structure at a time. Structure* m_structure; }; struct AbstractValue { AbstractValue() : m_type(PredictNone) { } void clear() { m_type = PredictNone; m_structure.clear(); m_value = JSValue(); checkConsistency(); } bool isClear() const { bool result = m_type == PredictNone && m_structure.isClear(); if (result) ASSERT(!m_value); return result; } void makeTop() { m_type = PredictTop; m_structure.makeTop(); m_value = JSValue(); checkConsistency(); } void clobberStructures() { if (m_type & PredictCell) m_structure.makeTop(); else ASSERT(m_structure.isClear()); checkConsistency(); } void clobberValue() { m_value = JSValue(); } bool isTop() const { return m_type == PredictTop && m_structure.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 set(JSValue value) { if (!!value && value.isCell()) { // Have to be careful here! It's tempting to set the structure to the // value's structure, but that would be wrong, since that would // constitute a proof that this value will always have the same // structure. The whole point of a value having a structure is that // it may change in the future - for example between when we compile // the code and when we run it. m_structure.makeTop(); } else m_structure.clear(); m_type = predictionFromValue(value); m_value = value; checkConsistency(); } void set(Structure* structure) { m_structure.clear(); m_structure.add(structure); m_type = predictionFromStructure(structure); m_value = JSValue(); checkConsistency(); } void set(PredictedType type) { if (type & PredictCell) m_structure.makeTop(); else m_structure.clear(); m_type = type; m_value = JSValue(); checkConsistency(); } bool operator==(const AbstractValue& other) const { return m_type == other.m_type && m_structure == other.m_structure && m_value == other.m_value; } bool operator!=(const AbstractValue& other) const { return !(*this == other); } bool merge(const AbstractValue& other) { #if !ASSERT_DISABLED AbstractValue oldMe = *this; #endif bool result = false; if (isClear()) { *this = other; result = !other.isClear(); } else { result |= mergePrediction(m_type, other.m_type); result |= m_structure.addAll(other.m_structure); if (m_value != other.m_value) { result |= !!m_value; m_value = JSValue(); } } checkConsistency(); ASSERT(result == (*this != oldMe)); return result; } void merge(PredictedType type) { mergePrediction(m_type, type); if (type & PredictCell) m_structure.makeTop(); m_value = JSValue(); checkConsistency(); } void filter(const StructureSet& other) { m_type &= other.predictionFromStructures(); m_structure.filter(other); // It's possible that prior to the above two statements we had (Foo, TOP), where // Foo is a PredictedType 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 PredictedType needs to be fed back // into the information gleaned from the StructureSet. m_structure.filter(m_type); if (!!m_value && !validateIgnoringValue(m_value)) clear(); checkConsistency(); } void filter(PredictedType type) { if (type == PredictTop) 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_structure.filter(m_type); if (!!m_value && !validateIgnoringValue(m_value)) clear(); checkConsistency(); } bool validateIgnoringValue(JSValue value) const { if (isTop()) return true; if (mergePredictions(m_type, predictionFromValue(value)) != m_type) return false; if (value.isEmpty()) { ASSERT(m_type & PredictEmpty); return true; } if (m_structure.isTop()) return true; if (!!value && value.isCell()) { ASSERT(m_type & PredictCell); return m_structure.contains(value.asCell()->structure()); } return true; } bool validate(JSValue value) const { if (isTop()) return true; if (!!m_value) return m_value == value; if (mergePredictions(m_type, predictionFromValue(value)) != m_type) return false; if (value.isEmpty()) { ASSERT(m_type & PredictEmpty); return true; } if (m_structure.isTop()) return true; if (!!value && value.isCell()) { ASSERT(m_type & PredictCell); return m_structure.contains(value.asCell()->structure()); } return true; } void checkConsistency() const { if (!(m_type & PredictCell)) ASSERT(m_structure.isClear()); if (isClear()) ASSERT(!m_value); if (!!m_value) ASSERT(mergePredictions(m_type, predictionFromValue(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(FILE* out) const { fprintf(out, "(%s, ", predictionToString(m_type)); m_structure.dump(out); if (!!m_value) fprintf(out, ", %s", m_value.description()); fprintf(out, ")"); } StructureAbstractValue m_structure; PredictedType m_type; JSValue m_value; }; } } // namespace JSC::DFG #endif // ENABLE(DFG_JIT) #endif // DFGAbstractValue_h