diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-08-21 10:57:44 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-08-21 10:57:44 +0200 |
commit | 5ef7c8a6a70875d4430752d146bdcb069605d71d (patch) | |
tree | f6256640b6c46d7da221435803cae65326817ba2 /Source/JavaScriptCore/dfg | |
parent | decad929f578d8db641febc8740649ca6c574638 (diff) | |
download | qtwebkit-5ef7c8a6a70875d4430752d146bdcb069605d71d.tar.gz |
Imported WebKit commit 356d83016b090995d08ad568f2d2c243aa55e831 (http://svn.webkit.org/repository/webkit/trunk@126147)
New snapshot including various build fixes for newer Qt 5
Diffstat (limited to 'Source/JavaScriptCore/dfg')
21 files changed, 948 insertions, 544 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp index 4f02ee793..5c53f6d78 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp @@ -62,13 +62,13 @@ void AbstractState::beginBasicBlock(BasicBlock* basicBlock) m_variables = basicBlock->valuesAtHead; m_haveStructures = false; for (size_t i = 0; i < m_variables.numberOfArguments(); ++i) { - if (m_variables.argument(i).m_structure.isNeitherClearNorTop()) { + if (m_variables.argument(i).m_currentKnownStructure.isNeitherClearNorTop()) { m_haveStructures = true; break; } } for (size_t i = 0; i < m_variables.numberOfLocals(); ++i) { - if (m_variables.local(i).m_structure.isNeitherClearNorTop()) { + if (m_variables.local(i).m_currentKnownStructure.isNeitherClearNorTop()) { m_haveStructures = true; break; } @@ -106,8 +106,6 @@ void AbstractState::initialize(Graph& graph) SpeculatedType prediction = node.variableAccessData()->prediction(); if (isInt32Speculation(prediction)) root->valuesAtHead.argument(i).set(SpecInt32); - else if (isArraySpeculation(prediction)) - root->valuesAtHead.argument(i).set(SpecArray); else if (isBooleanSpeculation(prediction)) root->valuesAtHead.argument(i).set(SpecBoolean); else if (isInt8ArraySpeculation(prediction)) @@ -160,6 +158,16 @@ void AbstractState::initialize(Graph& graph) block->valuesAtHead.local(i).clear(); block->valuesAtTail.local(i).clear(); } + if (!block->isOSRTarget) + continue; + if (block->bytecodeBegin != graph.m_osrEntryBytecodeIndex) + continue; + for (size_t i = 0; i < graph.m_mustHandleValues.size(); ++i) { + AbstractValue value; + value.setMostSpecific(graph.m_mustHandleValues[i]); + block->valuesAtHead.operand(graph.m_mustHandleValues.operandForIndex(i)).merge(value); + } + block->cfaShouldRevisit = true; } } @@ -290,10 +298,7 @@ bool AbstractState::execute(unsigned indexInBlock) SpeculatedType predictedType = node.variableAccessData()->argumentAwarePrediction(); if (isInt32Speculation(predictedType)) speculateInt32Unary(node); - else if (isArraySpeculation(predictedType)) { - node.setCanExit(!isArraySpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecArray); - } else if (isCellSpeculation(predictedType)) { + else if (isCellSpeculation(predictedType)) { node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type)); forNode(node.child1()).filter(SpecCell); } else if (isBooleanSpeculation(predictedType)) @@ -867,7 +872,7 @@ bool AbstractState::execute(unsigned indexInBlock) m_isValid = false; break; } - if (!isActionableArraySpeculation(m_graph[node.child1()].prediction()) || !m_graph[node.child2()].shouldSpeculateInteger()) { + if (!m_graph[node.child2()].shouldSpeculateInteger() || (!node.child3() && !m_graph[node.child1()].shouldSpeculateArguments())) { clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).makeTop(); break; @@ -942,8 +947,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(nodeIndex).set(SpecDouble); break; } - ASSERT(m_graph[node.child1()].shouldSpeculateArray()); - forNode(node.child1()).filter(SpecArray); + forNode(node.child1()).filter(SpecCell); forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).makeTop(); break; @@ -962,7 +966,7 @@ bool AbstractState::execute(unsigned indexInBlock) m_isValid = false; break; } - if (!m_graph[child2].shouldSpeculateInteger() || !isActionableMutableArraySpeculation(m_graph[child1].prediction()) + if (!m_graph[child2].shouldSpeculateInteger() #if USE(JSVALUE32_64) || m_graph[child1].shouldSpeculateArguments() #endif @@ -1053,8 +1057,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; } - ASSERT(m_graph[child1].shouldSpeculateArray()); - forNode(child1).filter(SpecArray); + forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); if (node.op() == PutByValSafe) clobberWorld(node.codeOrigin, indexInBlock); @@ -1063,13 +1066,13 @@ bool AbstractState::execute(unsigned indexInBlock) case ArrayPush: node.setCanExit(true); - forNode(node.child1()).filter(SpecArray); + forNode(node.child1()).filter(SpecCell); forNode(nodeIndex).set(SpecNumber); break; case ArrayPop: node.setCanExit(true); - forNode(node.child1()).filter(SpecArray); + forNode(node.child1()).filter(SpecCell); forNode(nodeIndex).makeTop(); break; @@ -1354,7 +1357,7 @@ bool AbstractState::execute(unsigned indexInBlock) case GetArrayLength: node.setCanExit(true); - forNode(node.child1()).filter(SpecArray); + forNode(node.child1()).filter(SpecCell); forNode(nodeIndex).set(SpecInt32); break; @@ -1420,19 +1423,52 @@ bool AbstractState::execute(unsigned indexInBlock) case ForwardCheckStructure: { // FIXME: We should be able to propagate the structure sets of constants (i.e. prototypes). AbstractValue& value = forNode(node.child1()); + // If this structure check is attempting to prove knowledge already held in + // the futurePossibleStructure set then the constant folding phase should + // turn this into a watchpoint instead. + StructureSet& set = node.structureSet(); + if (value.m_futurePossibleStructure.isSubsetOf(set)) + m_foundConstants = true; node.setCanExit( - !value.m_structure.isSubsetOf(node.structureSet()) + !value.m_currentKnownStructure.isSubsetOf(set) || !isCellSpeculation(value.m_type)); - value.filter(node.structureSet()); + value.filter(set); + // This is likely to be unnecessary, but it's conservative, and that's a good thing. + // This is trying to avoid situations where the CFA proves that this structure check + // must fail due to a future structure proof. We have two options at that point. We + // can either compile all subsequent code as we would otherwise, or we can ensure + // that the subsequent code is never reachable. The former is correct because the + // Proof Is Infallible (TM) -- hence even if we don't force the subsequent code to + // be unreachable, it must be unreachable nonetheless. But imagine what would happen + // if the proof was borked. In the former case, we'd get really bizarre bugs where + // we assumed that the structure of this object was known even though it wasn't. In + // the latter case, we'd have a slight performance pathology because this would be + // turned into an OSR exit unnecessarily. Which would you rather have? + if (value.m_currentKnownStructure.isClear() + || value.m_futurePossibleStructure.isClear()) + m_isValid = false; m_haveStructures = true; break; } - case StructureTransitionWatchpoint: { - // FIXME: Turn CheckStructure into StructureTransitionWatchpoint when possible! + case StructureTransitionWatchpoint: + case ForwardStructureTransitionWatchpoint: { AbstractValue& value = forNode(node.child1()); + + // It's only valid to issue a structure transition watchpoint if we already + // know that the watchpoint covers a superset of the structures known to + // belong to the set of future structures that this value may have. + // Currently, we only issue singleton watchpoints (that check one structure) + // and our futurePossibleStructure set can only contain zero, one, or an + // infinity of structures. + ASSERT(value.m_futurePossibleStructure.isSubsetOf(StructureSet(node.structure()))); + ASSERT(value.isClear() || isCellSpeculation(value.m_type)); // Value could be clear if we've proven must-exit due to a speculation statically known to be bad. value.filter(node.structure()); + // See comment in CheckStructure for why this is here. + if (value.m_currentKnownStructure.isClear() + || value.m_futurePossibleStructure.isClear()) + m_isValid = false; m_haveStructures = true; node.setCanExit(true); break; @@ -1453,12 +1489,9 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(nodeIndex).clear(); // The result is not a JS value. break; case GetIndexedPropertyStorage: { + ASSERT(m_graph[node.child1()].prediction()); + ASSERT(m_graph[node.child2()].shouldSpeculateInteger()); node.setCanExit(true); // Lies, but this is (almost) always followed by GetByVal, which does exit. So no point in trying to be more precise. - SpeculatedType basePrediction = m_graph[node.child2()].prediction(); - if (!(basePrediction & SpecInt32) && basePrediction) { - forNode(nodeIndex).clear(); - break; - } if (m_graph[node.child1()].shouldSpeculateArguments()) { ASSERT_NOT_REACHED(); break; @@ -1514,7 +1547,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(nodeIndex).clear(); break; } - forNode(node.child1()).filter(SpecArray); + forNode(node.child1()).filter(SpecCell); forNode(nodeIndex).clear(); break; } diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h index 402fd0fcd..ff1c6d205 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractValue.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,283 +30,13 @@ #if ENABLE(DFG_JIT) +#include "DFGStructureAbstractValue.h" #include "JSCell.h" #include "SpeculatedType.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(SpeculatedType other) - { - if (!(other & SpecCell)) { - clear(); - return; - } - - if (isClearOrTop()) - return; - - if (!(speculationFromStructure(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); - } - - SpeculatedType speculationFromStructures() const - { - if (isTop()) - return SpecCell; - if (isClear()) - return SpecNone; - return speculationFromStructure(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<Structure*>(1); } - - // This can only remember one structure at a time. - Structure* m_structure; -}; - struct AbstractValue { AbstractValue() : m_type(SpecNone) @@ -316,15 +46,15 @@ struct AbstractValue { void clear() { m_type = SpecNone; - m_structure.clear(); - m_unclobberedStructure.clear(); + m_currentKnownStructure.clear(); + m_futurePossibleStructure.clear(); m_value = JSValue(); checkConsistency(); } bool isClear() const { - bool result = m_type == SpecNone && m_structure.isClear() && m_unclobberedStructure.isClear(); + bool result = m_type == SpecNone && m_currentKnownStructure.isClear() && m_futurePossibleStructure.isClear(); if (result) ASSERT(!m_value); return result; @@ -333,8 +63,8 @@ struct AbstractValue { void makeTop() { m_type = SpecTop; - m_structure.makeTop(); - m_unclobberedStructure.makeTop(); + m_currentKnownStructure.makeTop(); + m_futurePossibleStructure.makeTop(); m_value = JSValue(); checkConsistency(); } @@ -342,9 +72,9 @@ struct AbstractValue { void clobberStructures() { if (m_type & SpecCell) - m_structure.makeTop(); + m_currentKnownStructure.makeTop(); else - ASSERT(m_structure.isClear()); + ASSERT(m_currentKnownStructure.isClear()); checkConsistency(); } @@ -355,7 +85,7 @@ struct AbstractValue { bool isTop() const { - return m_type == SpecTop && m_structure.isTop() && m_unclobberedStructure.isTop(); + return m_type == SpecTop && m_currentKnownStructure.isTop() && m_futurePossibleStructure.isTop(); } bool valueIsTop() const @@ -375,20 +105,29 @@ struct AbstractValue { return result; } - void set(JSValue value) + 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)); + } + + void setMostSpecific(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(); - m_unclobberedStructure.makeTop(); // FIXME: Consider not clobbering this. + Structure* structure = value.asCell()->structure(); + m_currentKnownStructure = structure; + setFuturePossibleStructure(structure); } else { - m_structure.clear(); - m_unclobberedStructure.clear(); + m_currentKnownStructure.clear(); + m_futurePossibleStructure.clear(); } m_type = speculationFromValue(value); @@ -397,14 +136,26 @@ struct AbstractValue { checkConsistency(); } - void set(Structure* structure) + void set(JSValue value) { - m_structure.clear(); - m_structure.add(structure); + if (!!value && value.isCell()) { + m_currentKnownStructure.makeTop(); + setFuturePossibleStructure(value.asCell()->structure()); + } else { + m_currentKnownStructure.clear(); + m_futurePossibleStructure.clear(); + } - m_unclobberedStructure.clear(); - m_unclobberedStructure.add(structure); + m_type = speculationFromValue(value); + m_value = value; + checkConsistency(); + } + + void set(Structure* structure) + { + m_currentKnownStructure = structure; + setFuturePossibleStructure(structure); m_type = speculationFromStructure(structure); m_value = JSValue(); @@ -414,11 +165,11 @@ struct AbstractValue { void set(SpeculatedType type) { if (type & SpecCell) { - m_structure.makeTop(); - m_unclobberedStructure.makeTop(); + m_currentKnownStructure.makeTop(); + m_futurePossibleStructure.makeTop(); } else { - m_structure.clear(); - m_unclobberedStructure.clear(); + m_currentKnownStructure.clear(); + m_futurePossibleStructure.clear(); } m_type = type; m_value = JSValue(); @@ -428,8 +179,8 @@ struct AbstractValue { bool operator==(const AbstractValue& other) const { return m_type == other.m_type - && m_structure == other.m_structure - && m_unclobberedStructure == other.m_unclobberedStructure + && m_currentKnownStructure == other.m_currentKnownStructure + && m_futurePossibleStructure == other.m_futurePossibleStructure && m_value == other.m_value; } bool operator!=(const AbstractValue& other) const @@ -448,8 +199,8 @@ struct AbstractValue { result = !other.isClear(); } else { result |= mergeSpeculation(m_type, other.m_type); - result |= m_structure.addAll(other.m_structure); - result |= m_unclobberedStructure.addAll(other.m_unclobberedStructure); + 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(); @@ -465,8 +216,8 @@ struct AbstractValue { mergeSpeculation(m_type, type); if (type & SpecCell) { - m_structure.makeTop(); - m_unclobberedStructure.makeTop(); + m_currentKnownStructure.makeTop(); + m_futurePossibleStructure.makeTop(); } m_value = JSValue(); @@ -476,19 +227,21 @@ struct AbstractValue { void filter(const StructureSet& other) { m_type &= other.speculationFromStructures(); - m_structure.filter(other); - m_unclobberedStructure.filter(other); + 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_structure.filter(m_type); - m_unclobberedStructure.filter(m_type); + m_currentKnownStructure.filter(m_type); + m_futurePossibleStructure.filter(m_type); - if (!!m_value && !validateIgnoringValue(m_value)) - clear(); + filterValueByType(); checkConsistency(); } @@ -503,47 +256,38 @@ struct AbstractValue { // 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); - m_unclobberedStructure.filter(m_type); + m_currentKnownStructure.filter(m_type); + m_futurePossibleStructure.filter(m_type); - if (!!m_value && !validateIgnoringValue(m_value)) - clear(); + filterValueByType(); checkConsistency(); } - bool validateIgnoringValue(JSValue value) const + // 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 (isTop()) - return true; - - if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type) - return false; - - if (value.isEmpty()) { - ASSERT(m_type & SpecEmpty); - return true; - } - - if (m_structure.isTop()) - return true; - - if (!!value && value.isCell()) { - ASSERT(m_type & SpecCell); - return m_structure.contains(value.asCell()->structure()); + 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; } - 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(); } - bool validate(JSValue value) const + bool validateType(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; @@ -552,18 +296,10 @@ struct AbstractValue { return true; } - if (m_structure.isTop()) - return true; - - if (!!value && value.isCell()) { - ASSERT(m_type & SpecCell); - return m_structure.contains(value.asCell()->structure()); - } - return true; } - bool validateForEntry(JSValue value) const + bool validate(JSValue value) const { if (isTop()) return true; @@ -579,12 +315,11 @@ struct AbstractValue { return true; } - if (m_unclobberedStructure.isTop()) - return true; - if (!!value && value.isCell()) { ASSERT(m_type & SpecCell); - return m_unclobberedStructure.contains(value.asCell()->structure()); + Structure* structure = value.asCell()->structure(); + return m_currentKnownStructure.contains(structure) + && m_futurePossibleStructure.contains(structure); } return true; @@ -593,8 +328,8 @@ struct AbstractValue { void checkConsistency() const { if (!(m_type & SpecCell)) { - ASSERT(m_structure.isClear()); - ASSERT(m_unclobberedStructure.isClear()); + ASSERT(m_currentKnownStructure.isClear()); + ASSERT(m_futurePossibleStructure.isClear()); } if (isClear()) @@ -612,17 +347,102 @@ struct AbstractValue { void dump(FILE* out) const { fprintf(out, "(%s, ", speculationToString(m_type)); - m_structure.dump(out); + m_currentKnownStructure.dump(out); dataLog(", "); - m_unclobberedStructure.dump(out); + m_futurePossibleStructure.dump(out); if (!!m_value) fprintf(out, ", %s", m_value.description()); fprintf(out, ")"); } + + // 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'. - StructureAbstractValue m_structure; - StructureAbstractValue m_unclobberedStructure; + // 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 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; }; diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp index 9208cde1b..000e1a938 100644 --- a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp @@ -317,6 +317,17 @@ public: // PhantomArguments and OSR exit will still do the right things. break; + case CheckStructure: + case ForwardCheckStructure: + case StructureTransitionWatchpoint: + case ForwardStructureTransitionWatchpoint: + // We don't care about these because if we get uses of the relevant + // variable then we can safely get rid of these, too. This of course + // relies on there not being any information transferred by the CFA + // from a CheckStructure on one variable to the information about the + // structures of another variable. + break; + default: observeBadArgumentsUses(node); break; @@ -471,6 +482,19 @@ public: break; } + case CheckStructure: + case ForwardCheckStructure: + case StructureTransitionWatchpoint: + case ForwardStructureTransitionWatchpoint: { + // We can just get rid of this node, if it references a phantom argument. + if (!isOKToOptimize(m_graph[node.child1()])) + break; + m_graph.deref(node.child1()); + node.setOpAndDefaultFlags(Phantom); + node.children.setChild1(Edge()); + break; + } + case GetByVal: { if (!node.prediction() || !m_graph[node.child1()].prediction() diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index b7f48aa4b..f7536f87f 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -118,7 +118,7 @@ private: template<PhiStackType stackType> void processPhiStack(); - void fixVariableAccessSpeculations(); + void fixVariableAccessPredictions(); // Add spill locations to nodes. void allocateVirtualRegisters(); @@ -2149,6 +2149,10 @@ bool ByteCodeParser::parseBlock(unsigned limit) NodeIndex base = get(currentInstruction[2].u.operand); NodeIndex property = get(currentInstruction[3].u.operand); + ArrayProfile* profile = currentInstruction[4].u.arrayProfile; + profile->computeUpdatedPrediction(); + if (profile->hasDefiniteStructure()) + addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(profile->expectedStructure())), base); NodeIndex propertyStorage = addToGraph(GetIndexedPropertyStorage, base, property); NodeIndex getByVal = addToGraph(GetByVal, OpInfo(0), OpInfo(prediction), base, property, propertyStorage); set(currentInstruction[1].u.operand, getByVal); @@ -2160,6 +2164,15 @@ bool ByteCodeParser::parseBlock(unsigned limit) NodeIndex base = get(currentInstruction[1].u.operand); NodeIndex property = get(currentInstruction[2].u.operand); NodeIndex value = get(currentInstruction[3].u.operand); + + ArrayProfile* profile = currentInstruction[4].u.arrayProfile; + profile->computeUpdatedPrediction(); + if (profile->hasDefiniteStructure()) + addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(profile->expectedStructure())), base); + +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + dataLog("Slow case profile for bc#%u: %u\n", m_currentIndex, m_inlineStackTop->m_profiledBlock->rareCaseProfileForBytecodeOffset(m_currentIndex)->m_counter); +#endif bool makeSafe = m_inlineStackTop->m_profiledBlock->couldTakeSlowCase(m_currentIndex) @@ -3017,7 +3030,7 @@ void ByteCodeParser::processPhiStack() } } -void ByteCodeParser::fixVariableAccessSpeculations() +void ByteCodeParser::fixVariableAccessPredictions() { for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) { VariableAccessData* data = &m_graph.m_variableAccessData[i]; @@ -3349,7 +3362,27 @@ bool ByteCodeParser::parse() m_graph.m_blocks[blockIndex].clear(); } - fixVariableAccessSpeculations(); + fixVariableAccessPredictions(); + + for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { + BasicBlock* block = m_graph.m_blocks[blockIndex].get(); + if (!block) + continue; + if (!block->isOSRTarget) + continue; + if (block->bytecodeBegin != m_graph.m_osrEntryBytecodeIndex) + continue; + for (size_t i = 0; i < m_graph.m_mustHandleValues.size(); ++i) { + NodeIndex nodeIndex = block->variablesAtHead.operand( + m_graph.m_mustHandleValues.operandForIndex(i)); + if (nodeIndex == NoNode) + continue; + Node& node = m_graph[nodeIndex]; + ASSERT(node.hasLocal()); + node.variableAccessData()->predict( + speculationFromValue(m_graph.m_mustHandleValues[i])); + } + } m_graph.m_preservedVars = m_preservedVars; m_graph.m_localVars = m_numLocals; diff --git a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp index dc1632dc4..f054707e2 100644 --- a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp @@ -613,7 +613,9 @@ private: ASSERT(node.shouldGenerate()); Node& possibleLocalOp = m_graph[node.child1()]; - if (possibleLocalOp.hasLocal() && !possibleLocalOp.variableAccessData()->isCaptured()) { + if (possibleLocalOp.op() != GetLocal + && possibleLocalOp.hasLocal() + && !possibleLocalOp.variableAccessData()->isCaptured()) { NodeIndex setLocalIndex = firstBlock->variablesAtTail.operand(possibleLocalOp.local()); Node& setLocal = m_graph[setLocalIndex]; diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp index e9b1e0d8b..b78ddc89d 100644 --- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp @@ -345,6 +345,7 @@ private: break; case StructureTransitionWatchpoint: + case ForwardStructureTransitionWatchpoint: if (node.child1() == child1 && structureSet.contains(node.structure())) return true; @@ -418,6 +419,7 @@ private: return false; case StructureTransitionWatchpoint: + case ForwardStructureTransitionWatchpoint: if (node.structure() == structure && node.child1() == child1) return true; break; @@ -843,6 +845,8 @@ private: // At this point we will eliminate all references to this node. m_replacements[m_compileIndex] = replacement; + m_changed = true; + return true; } @@ -856,6 +860,8 @@ private: ASSERT(node.refCount() == 1); ASSERT(node.mustGenerate()); node.setOpAndDefaultFlags(Phantom); + + m_changed = true; } void eliminate(NodeIndex nodeIndex, NodeType phantomType = Phantom) @@ -867,6 +873,8 @@ private: return; ASSERT(node.mustGenerate()); node.setOpAndDefaultFlags(phantomType); + + m_changed = true; } void performNodeCSE(Node& node) @@ -944,7 +952,7 @@ private: case GetLocal: { VariableAccessData* variableAccessData = node.variableAccessData(); - if (m_fixpointState == FixpointNotConverged && !variableAccessData->isCaptured()) + if (!variableAccessData->isCaptured()) break; NodeIndex relevantLocalOp; NodeIndex possibleReplacement = getLocalLoadElimination(variableAccessData->local(), relevantLocalOp, variableAccessData->isCaptured()); @@ -982,7 +990,7 @@ private: case GetLocalUnlinked: { NodeIndex relevantLocalOpIgnored; - m_changed |= setReplacement(getLocalLoadElimination(node.unlinkedLocal(), relevantLocalOpIgnored, true)); + setReplacement(getLocalLoadElimination(node.unlinkedLocal(), relevantLocalOpIgnored, true)); break; } @@ -1117,6 +1125,7 @@ private: break; case StructureTransitionWatchpoint: + case ForwardStructureTransitionWatchpoint: if (structureTransitionWatchpointElimination(node.structure(), node.child1().index())) eliminate(); break; diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp index a8eb9ee5c..68d5534e0 100644 --- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp @@ -91,7 +91,16 @@ private: break; } - // FIXME: This would be a great place to remove CheckStructure's. + case CheckStructure: + case ForwardCheckStructure: { + AbstractValue& value = m_state.forNode(node.child1()); + StructureAbstractValue& structureValue = value.m_futurePossibleStructure; + if (structureValue.isSubsetOf(node.structureSet()) + && structureValue.hasSingleton() + && isCellSpeculation(value.m_type)) + node.convertToStructureTransitionWatchpoint(structureValue.singleton()); + break; + } default: break; @@ -103,9 +112,7 @@ private: } m_state.execute(indexInBlock); - if (!node.shouldGenerate() - || m_state.didClobber() - || node.hasConstant()) + if (!node.shouldGenerate() || m_state.didClobber() || node.hasConstant()) continue; JSValue value = m_state.forNode(nodeIndex).value(); if (!value) diff --git a/Source/JavaScriptCore/dfg/DFGDriver.cpp b/Source/JavaScriptCore/dfg/DFGDriver.cpp index ddad4f864..ccef65208 100644 --- a/Source/JavaScriptCore/dfg/DFGDriver.cpp +++ b/Source/JavaScriptCore/dfg/DFGDriver.cpp @@ -26,6 +26,10 @@ #include "config.h" #include "DFGDriver.h" +#include "JSObject.h" +#include "JSString.h" +#include "ScopeChain.h" + #if ENABLE(DFG_JIT) #include "DFGArgumentsSimplificationPhase.h" @@ -53,7 +57,7 @@ unsigned getNumCompilations() } enum CompileMode { CompileFunction, CompileOther }; -inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode, MacroAssemblerCodePtr* jitCodeWithArityCheck) +inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode, MacroAssemblerCodePtr* jitCodeWithArityCheck, unsigned osrEntryBytecodeIndex) { SamplingRegion samplingRegion("DFG Compilation (Driver)"); @@ -62,6 +66,8 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo ASSERT(codeBlock); ASSERT(codeBlock->alternative()); ASSERT(codeBlock->alternative()->getJITType() == JITCode::BaselineJIT); + + ASSERT(osrEntryBytecodeIndex != UINT_MAX); if (!Options::useDFGJIT()) return false; @@ -70,7 +76,30 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo dataLog("DFG compiling code block %p(%p) for executable %p, number of instructions = %u.\n", codeBlock, codeBlock->alternative(), codeBlock->ownerExecutable(), codeBlock->instructionCount()); #endif - Graph dfg(exec->globalData(), codeBlock); + // Derive our set of must-handle values. The compilation must be at least conservative + // enough to allow for OSR entry with these values. + unsigned numVarsWithValues; + if (osrEntryBytecodeIndex) + numVarsWithValues = codeBlock->m_numVars; + else + numVarsWithValues = 0; + Operands<JSValue> mustHandleValues(codeBlock->numParameters(), numVarsWithValues); + for (size_t i = 0; i < mustHandleValues.size(); ++i) { + int operand = mustHandleValues.operandForIndex(i); + if (operandIsArgument(operand) + && !operandToArgument(operand) + && compileMode == CompileFunction + && codeBlock->specializationKind() == CodeForConstruct) { + // Ugh. If we're in a constructor, the 'this' argument may hold garbage. It will + // also never be used. It doesn't matter what we put into the value for this, + // but it has to be an actual value that can be grokked by subsequent DFG passes, + // so we sanitize it here by turning it into Undefined. + mustHandleValues[i] = jsUndefined(); + } else + mustHandleValues[i] = exec->uncheckedR(operand).jsValue(); + } + + Graph dfg(exec->globalData(), codeBlock, osrEntryBytecodeIndex, mustHandleValues); if (!parse(exec, dfg)) return false; @@ -86,6 +115,7 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo validate(dfg); performPredictionPropagation(dfg); performFixup(dfg); + performStructureCheckHoisting(dfg); unsigned cnt = 1; for (;; ++cnt) { #if DFG_ENABLE(DEBUG_VERBOSE) @@ -102,10 +132,7 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo dfg.resetExitStates(); performFixup(dfg); } - bool shouldRedoCFA = performStructureCheckHoisting(dfg); performCSE(dfg, FixpointConverged); - if (shouldRedoCFA) - performCFA(dfg); #if DFG_ENABLE(DEBUG_VERBOSE) dataLog("DFG optimization fixpoint converged in %u iterations.\n", cnt); #endif @@ -135,14 +162,14 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo return result; } -bool tryCompile(ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode) +bool tryCompile(ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode, unsigned bytecodeIndex) { - return compile(CompileOther, exec, codeBlock, jitCode, 0); + return compile(CompileOther, exec, codeBlock, jitCode, 0, bytecodeIndex); } -bool tryCompileFunction(ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode, MacroAssemblerCodePtr& jitCodeWithArityCheck) +bool tryCompileFunction(ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode, MacroAssemblerCodePtr& jitCodeWithArityCheck, unsigned bytecodeIndex) { - return compile(CompileFunction, exec, codeBlock, jitCode, &jitCodeWithArityCheck); + return compile(CompileFunction, exec, codeBlock, jitCode, &jitCodeWithArityCheck, bytecodeIndex); } } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGDriver.h b/Source/JavaScriptCore/dfg/DFGDriver.h index a6e82fef5..1964ec34a 100644 --- a/Source/JavaScriptCore/dfg/DFGDriver.h +++ b/Source/JavaScriptCore/dfg/DFGDriver.h @@ -41,11 +41,11 @@ namespace DFG { JS_EXPORT_PRIVATE unsigned getNumCompilations(); #if ENABLE(DFG_JIT) -bool tryCompile(ExecState*, CodeBlock*, JITCode&); -bool tryCompileFunction(ExecState*, CodeBlock*, JITCode&, MacroAssemblerCodePtr& jitCodeWithArityCheck); +bool tryCompile(ExecState*, CodeBlock*, JITCode&, unsigned bytecodeIndex); +bool tryCompileFunction(ExecState*, CodeBlock*, JITCode&, MacroAssemblerCodePtr& jitCodeWithArityCheck, unsigned bytecodeIndex); #else -inline bool tryCompile(ExecState*, CodeBlock*, JITCode&) { return false; } -inline bool tryCompileFunction(ExecState*, CodeBlock*, JITCode&, MacroAssemblerCodePtr&) { return false; } +inline bool tryCompile(ExecState*, CodeBlock*, JITCode&, unsigned) { return false; } +inline bool tryCompileFunction(ExecState*, CodeBlock*, JITCode&, MacroAssemblerCodePtr&, unsigned) { return false; } #endif } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index 4e3cd5782..f7b10fc43 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -96,9 +96,29 @@ private: #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" @%u -> %s", m_compileIndex, isArray ? "GetArrayLength" : "GetStringLength"); #endif - if (isArray) + if (isArray) { node.setOp(GetArrayLength); - else if (isArguments) + ASSERT(node.flags() & NodeMustGenerate); + node.clearFlags(NodeMustGenerate); + m_graph.deref(m_compileIndex); + + ArrayProfile* arrayProfile = + m_graph.baselineCodeBlockFor(node.codeOrigin)->getArrayProfile( + node.codeOrigin.bytecodeIndex); + if (!arrayProfile) + break; + arrayProfile->computeUpdatedPrediction(); + if (!arrayProfile->hasDefiniteStructure()) + break; + m_graph.ref(node.child1()); + Node checkStructure(CheckStructure, node.codeOrigin, OpInfo(m_graph.addStructureSet(arrayProfile->expectedStructure())), node.child1().index()); + checkStructure.ref(); + NodeIndex checkStructureIndex = m_graph.size(); + m_graph.append(checkStructure); + m_insertionSet.append(m_indexInBlock, checkStructureIndex); + break; + } + if (isArguments) node.setOp(GetArgumentsLength); else if (isString) node.setOp(GetStringLength); @@ -129,10 +149,9 @@ private: break; } case GetIndexedPropertyStorage: { - SpeculatedType basePrediction = m_graph[node.child2()].prediction(); - if ((!(basePrediction & SpecInt32) && basePrediction) - || m_graph[node.child1()].shouldSpeculateArguments() - || !isActionableArraySpeculation(m_graph[node.child1()].prediction())) { + if (!m_graph[node.child1()].prediction() + || !m_graph[node.child2()].shouldSpeculateInteger() + || m_graph[node.child1()].shouldSpeculateArguments()) { node.setOpAndDefaultFlags(Nop); m_graph.clearAndDerefChild1(node); m_graph.clearAndDerefChild2(node); diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h index fdb78cf9b..8d164a299 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.h +++ b/Source/JavaScriptCore/dfg/DFGGraph.h @@ -76,11 +76,13 @@ struct ResolveGlobalData { // Nodes that are 'dead' remain in the vector with refCount 0. class Graph : public Vector<Node, 64> { public: - Graph(JSGlobalData& globalData, CodeBlock* codeBlock) + Graph(JSGlobalData& globalData, CodeBlock* codeBlock, unsigned osrEntryBytecodeIndex, const Operands<JSValue>& mustHandleValues) : m_globalData(globalData) , m_codeBlock(codeBlock) , m_profiledBlock(codeBlock->alternative()) , m_hasArguments(false) + , m_osrEntryBytecodeIndex(osrEntryBytecodeIndex) + , m_mustHandleValues(mustHandleValues) { ASSERT(m_profiledBlock); } @@ -137,6 +139,20 @@ public: edge = newEdge; } + void compareAndSwap(Edge& edge, NodeIndex oldIndex, NodeIndex newIndex, bool changeRef) + { + if (edge.index() != oldIndex) + return; + changeIndex(edge, newIndex, changeRef); + } + + void compareAndSwap(Edge& edge, Edge oldEdge, Edge newEdge, bool changeRef) + { + if (edge != oldEdge) + return; + changeEdge(edge, newEdge, changeRef); + } + void clearAndDerefChild1(Node& node) { if (!node.child1()) @@ -614,6 +630,69 @@ public: vote(node.child3(), ballot); } + template<typename T> // T = NodeIndex or Edge + void substitute(BasicBlock& block, unsigned startIndexInBlock, T oldThing, T newThing) + { + for (unsigned indexInBlock = startIndexInBlock; indexInBlock < block.size(); ++indexInBlock) { + NodeIndex nodeIndex = block[indexInBlock]; + Node& node = at(nodeIndex); + if (node.flags() & NodeHasVarArgs) { + for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); ++childIdx) + compareAndSwap(m_varArgChildren[childIdx], oldThing, newThing, node.shouldGenerate()); + continue; + } + if (!node.child1()) + continue; + compareAndSwap(node.children.child1(), oldThing, newThing, node.shouldGenerate()); + if (!node.child2()) + continue; + compareAndSwap(node.children.child2(), oldThing, newThing, node.shouldGenerate()); + if (!node.child3()) + continue; + compareAndSwap(node.children.child3(), oldThing, newThing, node.shouldGenerate()); + } + } + + // Use this if you introduce a new GetLocal and you know that you introduced it *before* + // any GetLocals in the basic block. + // FIXME: it may be appropriate, in the future, to generalize this to handle GetLocals + // introduced anywhere in the basic block. + void substituteGetLocal(BasicBlock& block, unsigned startIndexInBlock, VariableAccessData* variableAccessData, NodeIndex newGetLocal) + { + if (variableAccessData->isCaptured()) { + // Let CSE worry about this one. + return; + } + for (unsigned indexInBlock = startIndexInBlock; indexInBlock < block.size(); ++indexInBlock) { + NodeIndex nodeIndex = block[indexInBlock]; + Node& node = at(nodeIndex); + bool shouldContinue = true; + switch (node.op()) { + case SetLocal: { + if (node.local() == variableAccessData->local()) + shouldContinue = false; + break; + } + + case GetLocal: { + if (node.variableAccessData() != variableAccessData) + continue; + substitute(block, indexInBlock, nodeIndex, newGetLocal); + NodeIndex oldTailIndex = block.variablesAtTail.operand(variableAccessData->local()); + if (oldTailIndex == nodeIndex) + block.variablesAtTail.operand(variableAccessData->local()) = newGetLocal; + shouldContinue = false; + break; + } + + default: + break; + } + if (!shouldContinue) + break; + } + } + JSGlobalData& m_globalData; CodeBlock* m_codeBlock; CodeBlock* m_profiledBlock; @@ -633,6 +712,8 @@ public: Dominators m_dominators; unsigned m_localVars; unsigned m_parameterSlots; + unsigned m_osrEntryBytecodeIndex; + Operands<JSValue> m_mustHandleValues; private: void handleSuccessor(Vector<BlockIndex, 16>& worklist, BlockIndex blockIndex, BlockIndex successorIndex); diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h index 60cdd4b50..dac855be0 100644 --- a/Source/JavaScriptCore/dfg/DFGNode.h +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -245,11 +245,19 @@ struct Node { children.reset(); } + void convertToStructureTransitionWatchpoint(Structure* structure) + { + ASSERT(m_op == CheckStructure || m_op == ForwardCheckStructure); + m_opInfo = bitwise_cast<uintptr_t>(structure); + if (m_op == CheckStructure) + m_op = StructureTransitionWatchpoint; + else + m_op = ForwardStructureTransitionWatchpoint; + } + void convertToStructureTransitionWatchpoint() { - ASSERT(m_op == CheckStructure); - m_opInfo = bitwise_cast<uintptr_t>(structureSet().singletonStructure()); - m_op = StructureTransitionWatchpoint; + convertToStructureTransitionWatchpoint(structureSet().singletonStructure()); } JSCell* weakConstant() @@ -675,7 +683,13 @@ struct Node { bool hasStructure() { - return op() == StructureTransitionWatchpoint; + switch (op()) { + case StructureTransitionWatchpoint: + case ForwardStructureTransitionWatchpoint: + return true; + default: + return false; + } } Structure* structure() diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h index 7657663a9..f0f8cb1d0 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeType.h +++ b/Source/JavaScriptCore/dfg/DFGNodeType.h @@ -133,6 +133,7 @@ namespace JSC { namespace DFG { /* the object's structure does not need to be rechecked due to side-effecting */\ /* (clobbering) operations. */\ macro(StructureTransitionWatchpoint, NodeMustGenerate) \ + macro(ForwardStructureTransitionWatchpoint, NodeMustGenerate) \ macro(PutStructure, NodeMustGenerate) \ macro(PhantomPutStructure, NodeMustGenerate | NodeDoesNotExit) \ macro(AllocatePropertyStorage, NodeMustGenerate | NodeDoesNotExit | NodeResultStorage) \ diff --git a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp index 97061bfb2..9a7bc96cc 100644 --- a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp @@ -99,7 +99,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn else value = exec->argument(argument - 1); - if (!entry->m_expectedValues.argument(argument).validateForEntry(value)) { + if (!entry->m_expectedValues.argument(argument).validate(value)) { #if ENABLE(JIT_VERBOSE_OSR) dataLog(" OSR failed because argument %zu is %s, expected ", argument, value.description()); entry->m_expectedValues.argument(argument).dump(WTF::dataFile()); @@ -119,7 +119,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn } continue; } - if (!entry->m_expectedValues.local(local).validateForEntry(exec->registers()[local].jsValue())) { + if (!entry->m_expectedValues.local(local).validate(exec->registers()[local].jsValue())) { #if ENABLE(JIT_VERBOSE_OSR) dataLog(" OSR failed because variable %zu is %s, expected ", local, exec->registers()[local].jsValue().description()); entry->m_expectedValues.local(local).dump(WTF::dataFile()); diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index 94f69abc2..1247528e8 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -675,6 +675,7 @@ private: case CheckStructure: case ForwardCheckStructure: case StructureTransitionWatchpoint: + case ForwardStructureTransitionWatchpoint: case CheckFunction: case PutStructure: case TearOffActivation: diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index f17e2d7e4..6c6615716 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -119,11 +119,8 @@ JumpReplacementWatchpoint* SpeculativeJIT::speculationWatchpoint(ExitKind kind) return speculationWatchpoint(kind, JSValueSource(), NoNode); } -void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, const ValueRecovery& valueRecovery) +void SpeculativeJIT::convertLastOSRExitToForward(const ValueRecovery& valueRecovery) { - ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); - speculationCheck(kind, jsValueSource, nodeIndex, jumpToFail); - #if !ASSERT_DISABLED if (!valueRecovery) { // Check that the preceding node was a SetLocal with the same code origin. @@ -155,6 +152,10 @@ void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValu ASSERT(setLocal->codeOrigin == at(m_compileIndex).codeOrigin); Node* nextNode = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock + 1)); + if (nextNode->op() == Jump && nextNode->codeOrigin == at(m_compileIndex).codeOrigin) { + // We're at an inlined return. Use a backward speculation instead. + return; + } ASSERT(nextNode->codeOrigin != at(m_compileIndex).codeOrigin); OSRExit& exit = m_jit.codeBlock()->lastOSRExit(); @@ -167,6 +168,28 @@ void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValu new ValueRecoveryOverride(setLocal->local(), valueRecovery)); } +JumpReplacementWatchpoint* SpeculativeJIT::forwardSpeculationWatchpoint(ExitKind kind) +{ + JumpReplacementWatchpoint* result = speculationWatchpoint(kind); + convertLastOSRExitToForward(); + return result; +} + +JumpReplacementWatchpoint* SpeculativeJIT::speculationWatchpointWithConditionalDirection(ExitKind kind, bool isForward) +{ + JumpReplacementWatchpoint* result = speculationWatchpoint(kind); + if (isForward) + convertLastOSRExitToForward(); + return result; +} + +void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, const ValueRecovery& valueRecovery) +{ + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); + speculationCheck(kind, jsValueSource, nodeIndex, jumpToFail); + convertLastOSRExitToForward(valueRecovery); +} + void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::JumpList& jumpsToFail, const ValueRecovery& valueRecovery) { ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); @@ -246,6 +269,24 @@ void SpeculativeJIT::clearGenerationInfo() m_fprs = RegisterBank<FPRInfo>(); } +void SpeculativeJIT::speculateArray(Edge edge, GPRReg baseReg) +{ + AbstractValue& arrayValue = m_state.forNode(edge); + if (arrayValue.m_currentKnownStructure.hasSingleton() + && arrayValue.m_currentKnownStructure.singleton()->classInfo() == &JSArray::s_info) + return; + + GPRTemporary temp(this); + m_jit.loadPtr( + MacroAssembler::Address(baseReg, JSCell::structureOffset()), temp.gpr()); + speculationCheck( + Uncountable, JSValueRegs(), NoNode, + m_jit.branchPtr( + MacroAssembler::NotEqual, + MacroAssembler::Address(temp.gpr(), Structure::classInfoOffset()), + MacroAssembler::TrustedImmPtr(&JSArray::s_info))); +} + GPRReg SpeculativeJIT::fillStorage(NodeIndex nodeIndex) { Node& node = m_jit.graph()[nodeIndex]; @@ -1220,7 +1261,7 @@ void SpeculativeJIT::compile(BasicBlock& block) valueSource = ValueSource(DoubleInRegisterFile); else if (isInt32Speculation(argumentPosition.prediction())) valueSource = ValueSource(Int32InRegisterFile); - else if (isArraySpeculation(argumentPosition.prediction()) || isCellSpeculation(argumentPosition.prediction())) + else if (isCellSpeculation(argumentPosition.prediction())) valueSource = ValueSource(CellInRegisterFile); else if (isBooleanSpeculation(argumentPosition.prediction())) valueSource = ValueSource(BooleanInRegisterFile); @@ -1343,12 +1384,7 @@ void SpeculativeJIT::checkArgumentTypes() #if USE(JSVALUE64) if (isInt32Speculation(predictedType)) speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::Below, JITCompiler::addressFor(virtualRegister), GPRInfo::tagTypeNumberRegister)); - else if (isArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); - } else if (isBooleanSpeculation(predictedType)) { + else if (isBooleanSpeculation(predictedType)) { GPRTemporary temp(this); m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); m_jit.xorPtr(TrustedImm32(static_cast<int32_t>(ValueFalse)), temp.gpr()); @@ -1403,13 +1439,7 @@ void SpeculativeJIT::checkArgumentTypes() #else if (isInt32Speculation(predictedType)) speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::Int32Tag))); - else if (isArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); - m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); - } else if (isBooleanSpeculation(predictedType)) + else if (isBooleanSpeculation(predictedType)) speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::BooleanTag))); else if (isInt8ArraySpeculation(predictedType)) { GPRTemporary temp(this); @@ -1591,7 +1621,6 @@ void SpeculativeJIT::compileGetByValOnString(Node& node) GPRReg storageReg = storage.gpr(); if (!isStringSpeculation(m_state.forNode(node.child1()).m_type)) { - ASSERT(!(at(node.child1()).prediction() & SpecString)); terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); noResult(m_compileIndex); return; @@ -3037,22 +3066,12 @@ bool SpeculativeJIT::compileStrictEq(Node& node) void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node) { - if (!node.prediction() || !at(node.child1()).prediction() || !at(node.child2()).prediction()) { - terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), NoNode); - return; - } + ASSERT(at(node.child1()).prediction()); + ASSERT(at(node.child2()).shouldSpeculateInteger()); SpeculateCellOperand base(this, node.child1()); GPRReg baseReg = base.gpr(); - SpeculatedType basePrediction = at(node.child2()).prediction(); - if (!(basePrediction & SpecInt32) && basePrediction) { - ASSERT_NOT_REACHED(); - terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); - noResult(m_compileIndex); - return; - } - GPRTemporary storage(this); GPRReg storageReg = storage.gpr(); if (at(node.child1()).shouldSpeculateArguments()) { @@ -3113,8 +3132,7 @@ void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node) speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo))); m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg); } else { - if (!isArraySpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + speculateArray(node.child1(), baseReg); m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg); } storageResult(storageReg, m_compileIndex); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 96f2fec0a..073dbb42c 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -2181,6 +2181,10 @@ public: // act of firing a watchpoint invalidates it. So, future recompilations will not // attempt to set this watchpoint again. JumpReplacementWatchpoint* speculationWatchpoint(ExitKind = UncountableWatchpoint); + + // It is generally a good idea to not use this directly. + void convertLastOSRExitToForward(const ValueRecovery& = ValueRecovery()); + // Note: not specifying the valueRecovery argument (leaving it as ValueRecovery()) implies // that you've ensured that there exists a MovHint prior to your use of forwardSpeculationCheck(). void forwardSpeculationCheck(ExitKind, JSValueSource, NodeIndex, MacroAssembler::Jump jumpToFail, const ValueRecovery& = ValueRecovery()); @@ -2190,6 +2194,12 @@ public: void terminateSpeculativeExecution(ExitKind, JSValueRegs, NodeIndex); void terminateSpeculativeExecution(ExitKind, JSValueRegs, Edge); void terminateSpeculativeExecutionWithConditionalDirection(ExitKind, JSValueRegs, NodeIndex, bool isForward); + // Issue a forward speculation watchpoint, which will exit to the next instruction rather + // than the current one. + JumpReplacementWatchpoint* forwardSpeculationWatchpoint(ExitKind = UncountableWatchpoint); + JumpReplacementWatchpoint* speculationWatchpointWithConditionalDirection(ExitKind, bool isForward); + + void speculateArray(Edge baseEdge, GPRReg baseReg); template<bool strict> GPRReg fillSpeculateIntInternal(NodeIndex, DataFormat& returnFormat); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index bf3503d6d..7a9ba1e41 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -1530,7 +1530,7 @@ void SpeculativeJIT::compileObjectToObjectOrOtherEquality( // We know that within this branch, rightChild must not be a cell. Check if that is enough to // prove that it is either null or undefined. - if (!isOtherSpeculation(m_state.forNode(rightChild).m_type & ~SpecCell)) { + if (!isOtherOrEmptySpeculation(m_state.forNode(rightChild).m_type & ~SpecCell)) { m_jit.move(op2TagGPR, resultGPR); m_jit.or32(TrustedImm32(1), resultGPR); @@ -1602,7 +1602,7 @@ void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality( // We know that within this branch, rightChild must not be a cell. Check if that is enough to // prove that it is either null or undefined. - if (isOtherSpeculation(m_state.forNode(rightChild).m_type & ~SpecCell)) + if (isOtherOrEmptySpeculation(m_state.forNode(rightChild).m_type & ~SpecCell)) rightNotCell.link(&m_jit); else { jump(notTaken, ForceJump); @@ -1918,7 +1918,7 @@ void SpeculativeJIT::compile(Node& node) break; } - if (isArraySpeculation(value.m_type) || isCellSpeculation(value.m_type)) { + if (isCellSpeculation(value.m_type)) { GPRTemporary result(this); m_jit.load32(JITCompiler::payloadFor(node.local()), result.gpr()); @@ -2036,16 +2036,6 @@ void SpeculativeJIT::compile(Node& node) recordSetLocal(node.local(), ValueSource(Int32InRegisterFile)); break; } - if (isArraySpeculation(predictedType)) { - SpeculateCellOperand cell(this, node.child1()); - GPRReg cellGPR = cell.gpr(); - if (!isArraySpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(cellGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(cellGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); - m_jit.storePtr(cellGPR, JITCompiler::payloadFor(node.local())); - noResult(m_compileIndex); - recordSetLocal(node.local(), ValueSource(CellInRegisterFile)); - break; - } if (isCellSpeculation(predictedType)) { SpeculateCellOperand cell(this, node.child1()); GPRReg cellGPR = cell.gpr(); @@ -2356,7 +2346,7 @@ void SpeculativeJIT::compile(Node& node) break; } - if (!at(node.child2()).shouldSpeculateInteger() || !isActionableArraySpeculation(at(node.child1()).prediction())) { + if (!at(node.child2()).shouldSpeculateInteger() || (!node.child3() && !at(node.child1()).shouldSpeculateArguments())) { SpeculateCellOperand base(this, node.child1()); // Save a register, speculate cell. We'll probably be right. JSValueOperand property(this, node.child2()); GPRReg baseGPR = base.gpr(); @@ -2449,8 +2439,6 @@ void SpeculativeJIT::compile(Node& node) break; } - ASSERT(at(node.child1()).shouldSpeculateArray()); - SpeculateStrictInt32Operand property(this, node.child2()); StorageOperand storage(this, node.child3()); GPRReg propertyReg = property.gpr(); @@ -2464,17 +2452,13 @@ void SpeculativeJIT::compile(Node& node) { SpeculateCellOperand base(this, node.child1()); GPRReg baseReg = base.gpr(); - if (!isArraySpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + // We've already speculated that it's some kind of array, at this point. speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset()))); } GPRTemporary resultTag(this); GPRTemporary resultPayload(this); - // FIXME: In cases where there are subsequent by_val accesses to the same base it might help to cache - // the storage pointer - especially if there happens to be another register free right now. If we do so, - // then we'll need to allocate a new temporary for result. m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag.gpr()); speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::Equal, resultTag.gpr(), TrustedImm32(JSValue::EmptyValueTag))); m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr()); @@ -2495,7 +2479,6 @@ void SpeculativeJIT::compile(Node& node) } if (!at(child2).shouldSpeculateInteger() - || !isActionableMutableArraySpeculation(at(child1).prediction()) || at(child1).shouldSpeculateArguments()) { SpeculateCellOperand base(this, child1); // Save a register, speculate cell. We'll probably be right. JSValueOperand property(this, child2); @@ -2578,27 +2561,23 @@ void SpeculativeJIT::compile(Node& node) break; } - ASSERT(at(child1).shouldSpeculateArray()); - JSValueOperand value(this, child3); - GPRTemporary scratch(this); - // Map base, property & value into registers, allocate a scratch register. GPRReg baseReg = base.gpr(); GPRReg propertyReg = property.gpr(); GPRReg valueTagReg = value.tagGPR(); GPRReg valuePayloadReg = value.payloadGPR(); - GPRReg scratchReg = scratch.gpr(); if (!m_compileOkay) return; - - writeBarrier(baseReg, valueTagReg, child3, WriteBarrierForPropertyAccess, scratchReg); - // Check that base is an array, and that property is contained within m_vector (< m_vectorLength). - // If we have predicted the base to be type array, we can skip the check. - if (!isArraySpeculation(m_state.forNode(child1).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), child1, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + { + GPRTemporary scratch(this); + GPRReg scratchReg = scratch.gpr(); + writeBarrier(baseReg, valueTagReg, child3, WriteBarrierForPropertyAccess, scratchReg); + } + + speculateArray(child1, baseReg); MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())); if (node.op() == PutByVal) @@ -2609,7 +2588,8 @@ void SpeculativeJIT::compile(Node& node) value.use(); // Get the array storage. - GPRReg storageReg = scratchReg; + GPRTemporary storage(this); + GPRReg storageReg = storage.gpr(); m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg); // Check if we're writing to a hole; if so increment m_numValuesInVector. @@ -2795,20 +2775,23 @@ void SpeculativeJIT::compile(Node& node) case ArrayPush: { SpeculateCellOperand base(this, node.child1()); JSValueOperand value(this, node.child2()); - GPRTemporary storage(this); GPRTemporary storageLength(this); GPRReg baseGPR = base.gpr(); GPRReg valueTagGPR = value.tagGPR(); GPRReg valuePayloadGPR = value.payloadGPR(); - GPRReg storageGPR = storage.gpr(); GPRReg storageLengthGPR = storageLength.gpr(); - writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, storageGPR, storageLengthGPR); + { + GPRTemporary scratch(this); + writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); + } - if (!isArraySpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + speculateArray(node.child1(), baseGPR); + GPRTemporary storage(this); + GPRReg storageGPR = storage.gpr(); + m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), storageGPR); m_jit.load32(MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), storageLengthGPR); @@ -2844,8 +2827,7 @@ void SpeculativeJIT::compile(Node& node) GPRReg storageGPR = storage.gpr(); GPRReg storageLengthGPR = storageLength.gpr(); - if (!isArraySpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + speculateArray(node.child1(), baseGPR); m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), storageGPR); m_jit.load32(MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), storageLengthGPR); @@ -3395,8 +3377,7 @@ void SpeculativeJIT::compile(Node& node) SpeculateCellOperand base(this, node.child1()); GPRReg baseGPR = base.gpr(); - if (!isArraySpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + speculateArray(node.child1(), baseGPR); GPRTemporary result(this); GPRReg resultGPR = result.gpr(); @@ -3478,7 +3459,7 @@ void SpeculativeJIT::compile(Node& node) case CheckStructure: case ForwardCheckStructure: { AbstractValue& value = m_state.forNode(node.child1()); - if (value.m_structure.isSubsetOf(node.structureSet()) + if (value.m_currentKnownStructure.isSubsetOf(node.structureSet()) && isCellSpeculation(value.m_type)) { noResult(m_compileIndex); break; @@ -3519,9 +3500,12 @@ void SpeculativeJIT::compile(Node& node) break; } - case StructureTransitionWatchpoint: { + case StructureTransitionWatchpoint: + case ForwardStructureTransitionWatchpoint: { m_jit.addWeakReference(node.structure()); - node.structure()->addTransitionWatchpoint(speculationWatchpoint(BadCache)); + node.structure()->addTransitionWatchpoint( + speculationWatchpointWithConditionalDirection( + BadCache, node.op() == ForwardStructureTransitionWatchpoint)); #if !ASSERT_DISABLED SpeculateCellOperand op1(this, node.child1()); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index 5541113f2..c2151088c 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -2072,16 +2072,6 @@ void SpeculativeJIT::compile(Node& node) recordSetLocal(node.local(), ValueSource(Int32InRegisterFile)); break; } - if (isArraySpeculation(predictedType)) { - SpeculateCellOperand cell(this, node.child1()); - GPRReg cellGPR = cell.gpr(); - if (!isArraySpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueRegs(cellGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(cellGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); - m_jit.storePtr(cellGPR, JITCompiler::addressFor(node.local())); - noResult(m_compileIndex); - recordSetLocal(node.local(), ValueSource(CellInRegisterFile)); - break; - } if (isCellSpeculation(predictedType)) { SpeculateCellOperand cell(this, node.child1()); GPRReg cellGPR = cell.gpr(); @@ -2389,7 +2379,7 @@ void SpeculativeJIT::compile(Node& node) break; } - if (!at(node.child2()).shouldSpeculateInteger() || !isActionableArraySpeculation(at(node.child1()).prediction())) { + if (!at(node.child2()).shouldSpeculateInteger() || (!node.child3() && !at(node.child1()).shouldSpeculateArguments())) { JSValueOperand base(this, node.child1()); JSValueOperand property(this, node.child2()); GPRReg baseGPR = base.gpr(); @@ -2480,8 +2470,6 @@ void SpeculativeJIT::compile(Node& node) break; } - ASSERT(at(node.child1()).shouldSpeculateArray()); - SpeculateCellOperand base(this, node.child1()); SpeculateStrictInt32Operand property(this, node.child2()); StorageOperand storage(this, node.child3()); @@ -2493,13 +2481,11 @@ void SpeculativeJIT::compile(Node& node) if (!m_compileOkay) return; - if (!isArraySpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueRegs(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + // We will have already speculated that the base is some kind of array, + // at this point. + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset()))); - // FIXME: In cases where there are subsequent by_val accesses to the same base it might help to cache - // the storage pointer - especially if there happens to be another register free right now. If we do so, - // then we'll need to allocate a new temporary for result. GPRTemporary result(this); m_jit.loadPtr(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), result.gpr()); speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::Zero, result.gpr())); @@ -2519,7 +2505,7 @@ void SpeculativeJIT::compile(Node& node) break; } - if (!at(child2).shouldSpeculateInteger() || !isActionableMutableArraySpeculation(at(child1).prediction())) { + if (!at(child2).shouldSpeculateInteger()) { JSValueOperand arg1(this, child1); JSValueOperand arg2(this, child2); JSValueOperand arg3(this, child3); @@ -2537,9 +2523,8 @@ void SpeculativeJIT::compile(Node& node) SpeculateCellOperand base(this, child1); SpeculateStrictInt32Operand property(this, child2); if (at(child1).shouldSpeculateArguments()) { + dataLog(" in here "); JSValueOperand value(this, child3); - SpeculateCellOperand base(this, child1); - SpeculateStrictInt32Operand property(this, child2); GPRTemporary scratch(this); GPRTemporary scratch2(this); @@ -2656,8 +2641,6 @@ void SpeculativeJIT::compile(Node& node) break; } - ASSERT(at(child1).shouldSpeculateArray()); - JSValueOperand value(this, child3); GPRTemporary scratch(this); @@ -2672,11 +2655,8 @@ void SpeculativeJIT::compile(Node& node) writeBarrier(baseReg, value.gpr(), child3, WriteBarrierForPropertyAccess, scratchReg); - // Check that base is an array, and that property is contained within m_vector (< m_vectorLength). - // If we have predicted the base to be type array, we can skip the check. - if (!isArraySpeculation(m_state.forNode(child1).m_type)) - speculationCheck(BadType, JSValueRegs(baseReg), child1, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); - + speculateArray(child1, baseReg); + MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())); if (node.op() == PutByVal) speculationCheck(OutOfBounds, JSValueRegs(), NoNode, beyondArrayBounds); @@ -2879,9 +2859,8 @@ void SpeculativeJIT::compile(Node& node) writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, storageGPR, storageLengthGPR); - if (!isArraySpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueRegs(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); - + speculateArray(node.child1(), baseGPR); + m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), storageGPR); m_jit.load32(MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), storageLengthGPR); @@ -2917,9 +2896,8 @@ void SpeculativeJIT::compile(Node& node) GPRReg storageGPR = storage.gpr(); GPRReg storageLengthGPR = storageLength.gpr(); - if (!isArraySpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueRegs(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); - + speculateArray(node.child1(), baseGPR); + m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), storageGPR); m_jit.load32(MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), storageLengthGPR); @@ -3415,9 +3393,8 @@ void SpeculativeJIT::compile(Node& node) GPRReg baseGPR = base.gpr(); GPRReg resultGPR = result.gpr(); - if (!isArraySpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueRegs(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); - + speculateArray(node.child1(), baseGPR); + m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), resultGPR); m_jit.load32(MacroAssembler::Address(resultGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), resultGPR); @@ -3493,7 +3470,7 @@ void SpeculativeJIT::compile(Node& node) case CheckStructure: case ForwardCheckStructure: { AbstractValue& value = m_state.forNode(node.child1()); - if (value.m_structure.isSubsetOf(node.structureSet()) + if (value.m_currentKnownStructure.isSubsetOf(node.structureSet()) && isCellSpeculation(value.m_type)) { noResult(m_compileIndex); break; @@ -3534,9 +3511,12 @@ void SpeculativeJIT::compile(Node& node) break; } - case StructureTransitionWatchpoint: { + case StructureTransitionWatchpoint: + case ForwardStructureTransitionWatchpoint: { m_jit.addWeakReference(node.structure()); - node.structure()->addTransitionWatchpoint(speculationWatchpoint(BadCache)); + node.structure()->addTransitionWatchpoint( + speculationWatchpointWithConditionalDirection( + BadCache, node.op() == ForwardStructureTransitionWatchpoint)); #if !ASSERT_DISABLED SpeculateCellOperand op1(this, node.child1()); diff --git a/Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h b/Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h new file mode 100644 index 000000000..b3082de1a --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2011, 2012 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 DFGStructureAbstractValue_h +#define DFGStructureAbstractValue_h + +#include <wtf/Platform.h> + +#if ENABLE(DFG_JIT) + +#include "JSCell.h" +#include "SpeculatedType.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(SpeculatedType other) + { + if (!(other & SpecCell)) { + clear(); + return; + } + + if (isClearOrTop()) + return; + + if (!(speculationFromStructure(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); + } + + SpeculatedType speculationFromStructures() const + { + if (isTop()) + return SpecCell; + if (isClear()) + return SpecNone; + return speculationFromStructure(m_structure); + } + + bool hasSingleton() const + { + return isNeitherClearNorTop(); + } + + Structure* singleton() const + { + ASSERT(isNeitherClearNorTop()); + return 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<Structure*>(1); } + + // This can only remember one structure at a time. + Structure* m_structure; +}; + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + +#endif // DFGStructureAbstractValue_h + + diff --git a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp index e86c57dff..68627f95c 100644 --- a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp @@ -82,6 +82,7 @@ public: } case ForwardCheckStructure: + case ForwardStructureTransitionWatchpoint: // We currently rely on the fact that we're the only ones who would // insert this node. ASSERT_NOT_REACHED(); @@ -94,6 +95,12 @@ public: case AllocatePropertyStorage: case ReallocatePropertyStorage: case GetPropertyStorage: + case GetByVal: + case PutByVal: + case PutByValAlias: + case PutByValSafe: + case GetArrayLength: + case Phantom: // Don't count these uses. break; @@ -215,17 +222,22 @@ public: break; case PutByVal: - case PutByValAlias: { + case PutByValAlias: + case PutByValSafe: { Edge child1 = m_graph.varArgChild(node, 0); Edge child2 = m_graph.varArgChild(node, 1); if (!m_graph[child1].prediction() || !m_graph[child2].prediction()) break; - if (!m_graph[child2].shouldSpeculateInteger() || !isActionableMutableArraySpeculation(m_graph[child1].prediction())) { + if (!m_graph[child2].shouldSpeculateInteger() +#if USE(JSVALUE32_64) + || m_graph[child1].shouldSpeculateArguments() +#endif + ) { clobber(live); break; } - if (node.op() == PutByValAlias) + if (node.op() != PutByValSafe) break; if (m_graph[child1].shouldSpeculateArguments()) break; @@ -291,7 +303,7 @@ public: dataLog("Hoisting checks for %s\n", m_graph.nameOfVariableAccessData(it->first)); } #endif // DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - + // Make changes: // 1) If a variable's live range does not span a clobber, then inject structure // checks before the SetLocal. @@ -348,6 +360,8 @@ public: if (block->variablesAtTail.operand(variable->local()) == nodeIndex) block->variablesAtTail.operand(variable->local()) = getLocalIndex; + m_graph.substituteGetLocal(*block, indexInBlock, variable, getLocalIndex); + changed = true; break; } |