| /* |
| * Copyright (C) 2013-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 |
| * 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. |
| */ |
| |
| #include "config.h" |
| #include "DFGAbstractValue.h" |
| |
| #if ENABLE(DFG_JIT) |
| |
| #include "DFGGraph.h" |
| #include "JSCInlines.h" |
| #include "TrackedReferences.h" |
| |
| namespace JSC { namespace DFG { |
| |
| void AbstractValue::observeTransitions(const TransitionVector& vector) |
| { |
| if (m_type & SpecCell) { |
| m_structure.observeTransitions(vector); |
| ArrayModes newModes = 0; |
| for (unsigned i = vector.size(); i--;) { |
| if (m_arrayModes & asArrayModes(vector[i].previous->indexingType())) |
| newModes |= asArrayModes(vector[i].next->indexingType()); |
| } |
| m_arrayModes |= newModes; |
| } |
| checkConsistency(); |
| } |
| |
| void AbstractValue::set(Graph& graph, const FrozenValue& value, StructureClobberState clobberState) |
| { |
| if (!!value && value.value().isCell()) { |
| Structure* structure = value.structure(); |
| StructureRegistrationResult result; |
| RegisteredStructure RegisteredStructure = graph.registerStructure(structure, result); |
| if (result == StructureRegisteredAndWatched) { |
| m_structure = RegisteredStructure; |
| if (clobberState == StructuresAreClobbered) { |
| m_arrayModes = ALL_ARRAY_MODES; |
| m_structure.clobber(); |
| } else |
| m_arrayModes = asArrayModes(structure->indexingType()); |
| } else { |
| m_structure.makeTop(); |
| m_arrayModes = ALL_ARRAY_MODES; |
| } |
| } else { |
| m_structure.clear(); |
| m_arrayModes = 0; |
| } |
| |
| m_type = speculationFromValue(value.value()); |
| m_value = value.value(); |
| |
| checkConsistency(); |
| assertIsRegistered(graph); |
| } |
| |
| void AbstractValue::set(Graph& graph, Structure* structure) |
| { |
| set(graph, graph.registerStructure(structure)); |
| } |
| |
| void AbstractValue::set(Graph& graph, RegisteredStructure structure) |
| { |
| RELEASE_ASSERT(structure); |
| |
| m_structure = structure; |
| m_arrayModes = asArrayModes(structure->indexingType()); |
| m_type = speculationFromStructure(structure.get()); |
| m_value = JSValue(); |
| |
| checkConsistency(); |
| assertIsRegistered(graph); |
| } |
| |
| void AbstractValue::set(Graph& graph, const RegisteredStructureSet& set) |
| { |
| m_structure = set; |
| m_arrayModes = set.arrayModesFromStructures(); |
| m_type = set.speculationFromStructures(); |
| m_value = JSValue(); |
| |
| checkConsistency(); |
| assertIsRegistered(graph); |
| } |
| |
| void AbstractValue::setType(Graph& graph, SpeculatedType type) |
| { |
| SpeculatedType cellType = type & SpecCell; |
| if (cellType) { |
| if (!(cellType & ~SpecString)) |
| m_structure = graph.stringStructure; |
| else if (isSymbolSpeculation(cellType)) |
| m_structure = graph.symbolStructure; |
| else |
| m_structure.makeTop(); |
| m_arrayModes = ALL_ARRAY_MODES; |
| } else { |
| m_structure.clear(); |
| m_arrayModes = 0; |
| } |
| m_type = type; |
| m_value = JSValue(); |
| checkConsistency(); |
| } |
| |
| void AbstractValue::set(Graph& graph, const InferredType::Descriptor& descriptor) |
| { |
| switch (descriptor.kind()) { |
| case InferredType::Bottom: |
| clear(); |
| return; |
| case InferredType::Boolean: |
| setType(SpecBoolean); |
| return; |
| case InferredType::Other: |
| setType(SpecOther); |
| return; |
| case InferredType::Int32: |
| setType(SpecInt32Only); |
| return; |
| case InferredType::Number: |
| setType(SpecBytecodeNumber); |
| return; |
| case InferredType::String: |
| set(graph, graph.m_vm.stringStructure.get()); |
| return; |
| case InferredType::Symbol: |
| set(graph, graph.m_vm.symbolStructure.get()); |
| return; |
| case InferredType::BigInt: |
| set(graph, graph.m_vm.bigIntStructure.get()); |
| return; |
| case InferredType::ObjectWithStructure: |
| set(graph, descriptor.structure()); |
| return; |
| case InferredType::ObjectWithStructureOrOther: |
| set(graph, descriptor.structure()); |
| merge(SpecOther); |
| return; |
| case InferredType::Object: |
| setType(graph, SpecObject); |
| return; |
| case InferredType::ObjectOrOther: |
| setType(graph, SpecObject | SpecOther); |
| return; |
| case InferredType::Top: |
| makeHeapTop(); |
| return; |
| } |
| |
| RELEASE_ASSERT_NOT_REACHED(); |
| } |
| |
| void AbstractValue::set( |
| Graph& graph, const InferredType::Descriptor& descriptor, StructureClobberState clobberState) |
| { |
| set(graph, descriptor); |
| if (clobberState == StructuresAreClobbered) |
| clobberStructures(); |
| } |
| |
| void AbstractValue::fixTypeForRepresentation(Graph& graph, NodeFlags representation, Node* node) |
| { |
| if (representation == NodeResultDouble) { |
| if (m_value) { |
| ASSERT(m_value.isNumber()); |
| if (m_value.isInt32()) |
| m_value = jsDoubleNumber(m_value.asNumber()); |
| } |
| if (m_type & SpecAnyInt) { |
| m_type &= ~SpecAnyInt; |
| m_type |= SpecAnyIntAsDouble; |
| } |
| if (m_type & ~SpecFullDouble) |
| DFG_CRASH(graph, node, toCString("Abstract value ", *this, " for double node has type outside SpecFullDouble.\n").data()); |
| } else if (representation == NodeResultInt52) { |
| if (m_type & SpecAnyIntAsDouble) { |
| m_type &= ~SpecAnyIntAsDouble; |
| m_type |= SpecInt52Only; |
| } |
| if (m_type & ~SpecAnyInt) |
| DFG_CRASH(graph, node, toCString("Abstract value ", *this, " for int52 node has type outside SpecAnyInt.\n").data()); |
| } else { |
| if (m_type & SpecInt52Only) { |
| m_type &= ~SpecInt52Only; |
| m_type |= SpecAnyIntAsDouble; |
| } |
| if (m_type & ~SpecBytecodeTop) |
| DFG_CRASH(graph, node, toCString("Abstract value ", *this, " for value node has type outside SpecBytecodeTop.\n").data()); |
| } |
| |
| checkConsistency(); |
| } |
| |
| void AbstractValue::fixTypeForRepresentation(Graph& graph, Node* node) |
| { |
| fixTypeForRepresentation(graph, node->result(), node); |
| } |
| |
| bool AbstractValue::mergeOSREntryValue(Graph& graph, JSValue value) |
| { |
| AbstractValue oldMe = *this; |
| |
| if (isClear()) { |
| FrozenValue* frozenValue = graph.freeze(value); |
| if (frozenValue->pointsToHeap()) { |
| m_structure = graph.registerStructure(frozenValue->structure()); |
| m_arrayModes = asArrayModes(frozenValue->structure()->indexingType()); |
| } else { |
| m_structure.clear(); |
| m_arrayModes = 0; |
| } |
| |
| m_type = speculationFromValue(value); |
| m_value = value; |
| } else { |
| mergeSpeculation(m_type, speculationFromValue(value)); |
| if (!!value && value.isCell()) { |
| RegisteredStructure structure = graph.registerStructure(value.asCell()->structure()); |
| mergeArrayModes(m_arrayModes, asArrayModes(structure->indexingType())); |
| m_structure.merge(RegisteredStructureSet(structure)); |
| } |
| if (m_value != value) |
| m_value = JSValue(); |
| } |
| |
| checkConsistency(); |
| assertIsRegistered(graph); |
| |
| return oldMe != *this; |
| } |
| |
| bool AbstractValue::isType(Graph& graph, const InferredType::Descriptor& inferredType) const |
| { |
| AbstractValue typeValue; |
| typeValue.set(graph, inferredType); |
| |
| AbstractValue mergedValue = *this; |
| mergedValue.merge(typeValue); |
| |
| return mergedValue == typeValue; |
| } |
| |
| FiltrationResult AbstractValue::filter( |
| Graph& graph, const RegisteredStructureSet& other, SpeculatedType admittedTypes) |
| { |
| ASSERT(!(admittedTypes & SpecCell)); |
| |
| if (isClear()) |
| return FiltrationOK; |
| |
| // 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() | admittedTypes; |
| m_arrayModes &= other.arrayModesFromStructures(); |
| m_structure.filter(other); |
| |
| // 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 RegisteredStructureSet. 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 RegisteredStructureSet. |
| m_structure.filter(m_type); |
| |
| filterArrayModesByType(); |
| filterValueByType(); |
| return normalizeClarity(graph); |
| } |
| |
| FiltrationResult AbstractValue::changeStructure(Graph& graph, const RegisteredStructureSet& other) |
| { |
| m_type &= other.speculationFromStructures(); |
| m_arrayModes = other.arrayModesFromStructures(); |
| m_structure = other; |
| |
| filterValueByType(); |
| |
| return normalizeClarity(graph); |
| } |
| |
| FiltrationResult AbstractValue::filterArrayModes(ArrayModes arrayModes) |
| { |
| ASSERT(arrayModes); |
| |
| if (isClear()) |
| return FiltrationOK; |
| |
| m_type &= SpecCell; |
| m_arrayModes &= arrayModes; |
| return normalizeClarity(); |
| } |
| |
| FiltrationResult AbstractValue::filterClassInfo(Graph& graph, const ClassInfo* classInfo) |
| { |
| // FIXME: AI should track ClassInfo to leverage hierarchical class information. |
| // https://bugs.webkit.org/show_bug.cgi?id=162989 |
| if (isClear()) |
| return FiltrationOK; |
| |
| m_type &= speculationFromClassInfo(classInfo); |
| m_structure.filterClassInfo(classInfo); |
| |
| m_structure.filter(m_type); |
| |
| filterArrayModesByType(); |
| filterValueByType(); |
| return normalizeClarity(graph); |
| } |
| |
| FiltrationResult AbstractValue::filter(SpeculatedType type) |
| { |
| if ((m_type & type) == m_type) |
| return FiltrationOK; |
| |
| // Fast path for the case that we don't even have a cell. |
| if (!(m_type & SpecCell)) { |
| m_type &= type; |
| FiltrationResult result; |
| if (m_type == SpecNone) { |
| clear(); |
| result = Contradiction; |
| } else |
| result = FiltrationOK; |
| checkConsistency(); |
| return result; |
| } |
| |
| 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); |
| filterArrayModesByType(); |
| filterValueByType(); |
| return normalizeClarity(); |
| } |
| |
| FiltrationResult AbstractValue::filterByValue(const FrozenValue& value) |
| { |
| FiltrationResult result = filter(speculationFromValue(value.value())); |
| if (m_type) |
| m_value = value.value(); |
| return result; |
| } |
| |
| bool AbstractValue::contains(RegisteredStructure structure) const |
| { |
| return couldBeType(speculationFromStructure(structure.get())) |
| && (m_arrayModes & arrayModeFromStructure(structure.get())) |
| && m_structure.contains(structure); |
| } |
| |
| FiltrationResult AbstractValue::filter(const AbstractValue& other) |
| { |
| m_type &= other.m_type; |
| m_structure.filter(other.m_structure); |
| m_arrayModes &= other.m_arrayModes; |
| |
| m_structure.filter(m_type); |
| filterArrayModesByType(); |
| filterValueByType(); |
| |
| if (normalizeClarity() == Contradiction) |
| return Contradiction; |
| |
| if (m_value == other.m_value) |
| return FiltrationOK; |
| |
| // Neither of us are BOTTOM, so an empty value means TOP. |
| if (!m_value) { |
| // We previously didn't prove a value but now we have done so. |
| m_value = other.m_value; |
| return FiltrationOK; |
| } |
| |
| if (!other.m_value) { |
| // We had proved a value but the other guy hadn't, so keep our proof. |
| return FiltrationOK; |
| } |
| |
| // We both proved there to be a specific value but they are different. |
| clear(); |
| return Contradiction; |
| } |
| |
| FiltrationResult AbstractValue::filter(Graph& graph, const InferredType::Descriptor& descriptor) |
| { |
| AbstractValue filterValue; |
| filterValue.set(graph, descriptor); |
| return filter(filterValue); |
| } |
| |
| void AbstractValue::filterValueByType() |
| { |
| // 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. |
| |
| if (!!m_type) { |
| // The type is still non-empty. It may be that the new type renders |
| // the value empty because it contravenes the constant value we had. |
| if (m_value && !validateType(m_value)) |
| clear(); |
| 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 AbstractValue::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; |
| } |
| |
| bool AbstractValue::shouldBeClear() const |
| { |
| if (m_type == SpecNone) |
| return true; |
| |
| if (!(m_type & ~SpecCell) |
| && (!m_arrayModes || m_structure.isClear())) |
| return true; |
| |
| return false; |
| } |
| |
| FiltrationResult AbstractValue::normalizeClarity() |
| { |
| // It's useful to be able to quickly check if an abstract value is clear. |
| // This normalizes everything to make that easy. |
| |
| FiltrationResult result; |
| |
| if (shouldBeClear()) { |
| clear(); |
| result = Contradiction; |
| } else |
| result = FiltrationOK; |
| |
| checkConsistency(); |
| |
| return result; |
| } |
| |
| FiltrationResult AbstractValue::normalizeClarity(Graph& graph) |
| { |
| FiltrationResult result = normalizeClarity(); |
| assertIsRegistered(graph); |
| return result; |
| } |
| |
| #if !ASSERT_DISABLED |
| void AbstractValue::checkConsistency() const |
| { |
| if (!(m_type & SpecCell)) { |
| ASSERT(m_structure.isClear()); |
| ASSERT(!m_arrayModes); |
| } |
| |
| if (isClear()) |
| ASSERT(!m_value); |
| |
| if (!!m_value) { |
| SpeculatedType type = m_type; |
| // This relaxes the assertion below a bit, since we don't know the representation of the |
| // node. |
| if (type & SpecInt52Only) |
| type |= SpecAnyIntAsDouble; |
| ASSERT(mergeSpeculations(type, speculationFromValue(m_value)) == 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 AbstractValue::assertIsRegistered(Graph& graph) const |
| { |
| m_structure.assertIsRegistered(graph); |
| } |
| #endif |
| |
| ResultType AbstractValue::resultType() const |
| { |
| ASSERT(isType(SpecBytecodeTop)); |
| if (isType(SpecBoolean)) |
| return ResultType::booleanType(); |
| if (isType(SpecInt32Only)) |
| return ResultType::numberTypeIsInt32(); |
| if (isType(SpecBytecodeNumber)) |
| return ResultType::numberType(); |
| if (isType(SpecString)) |
| return ResultType::stringType(); |
| if (isType(SpecString | SpecBytecodeNumber)) |
| return ResultType::stringOrNumberType(); |
| return ResultType::unknownType(); |
| } |
| |
| void AbstractValue::dump(PrintStream& out) const |
| { |
| dumpInContext(out, 0); |
| } |
| |
| void AbstractValue::dumpInContext(PrintStream& out, DumpContext* context) const |
| { |
| out.print("(", SpeculationDump(m_type)); |
| if (m_type & SpecCell) { |
| out.print( |
| ", ", ArrayModesDump(m_arrayModes), ", ", |
| inContext(m_structure, context)); |
| } |
| if (!!m_value) |
| out.print(", ", inContext(m_value, context)); |
| out.print(")"); |
| } |
| |
| void AbstractValue::validateReferences(const TrackedReferences& trackedReferences) |
| { |
| trackedReferences.check(m_value); |
| m_structure.validateReferences(trackedReferences); |
| } |
| |
| #if USE(JSVALUE64) && !defined(NDEBUG) |
| void AbstractValue::ensureCanInitializeWithZeros() |
| { |
| std::aligned_storage<sizeof(AbstractValue), alignof(AbstractValue)>::type zeroFilledStorage; |
| memset(static_cast<void*>(&zeroFilledStorage), 0, sizeof(AbstractValue)); |
| ASSERT(*this == *static_cast<AbstractValue*>(static_cast<void*>(&zeroFilledStorage))); |
| } |
| #endif |
| |
| } } // namespace JSC::DFG |
| |
| #endif // ENABLE(DFG_JIT) |
| |