diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2012-11-07 11:22:47 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2012-11-07 11:22:47 +0100 |
commit | cfd86b747d32ac22246a1aa908eaa720c63a88c1 (patch) | |
tree | 24d68c6f61c464ecba1e05670b80390ea3b0e50c /Source/JavaScriptCore/dfg | |
parent | 69d7c744c9de19d152dbe2d8e46eb7dfd4511d1a (diff) | |
download | qtwebkit-cfd86b747d32ac22246a1aa908eaa720c63a88c1.tar.gz |
Imported WebKit commit 20271caf2e2c016d5cef40184cddeefeac4f1876 (http://svn.webkit.org/repository/webkit/trunk@133733)
New snapshot that contains all previous fixes as well as build fix for latest QtMultimedia API changes.
Diffstat (limited to 'Source/JavaScriptCore/dfg')
30 files changed, 1158 insertions, 1280 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp index 58ff7d23c..e518c24a8 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp @@ -839,7 +839,7 @@ bool AbstractState::execute(unsigned indexInBlock) case GetByVal: { node.setCanExit(true); - switch (node.arrayMode()) { + switch (node.arrayMode().type()) { case Array::SelectUsingPredictions: case Array::Unprofiled: ASSERT_NOT_REACHED(); @@ -859,18 +859,12 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).makeTop(); break; - case IN_BOUNDS_CONTIGUOUS_MODES: - case IN_BOUNDS_ARRAY_STORAGE_MODES: + case Array::Contiguous: + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: forNode(node.child2()).filter(SpecInt32); - forNode(nodeIndex).makeTop(); - break; - case OUT_OF_BOUNDS_CONTIGUOUS_MODES: - case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: - case SLOW_PUT_ARRAY_STORAGE_MODES: - case ALL_EFFECTFUL_MODES: - forNode(node.child1()).filter(SpecCell); - forNode(node.child2()).filter(SpecInt32); - clobberWorld(node.codeOrigin, indexInBlock); + if (node.arrayMode().isOutOfBounds()) + clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).makeTop(); break; case Array::Int8Array: @@ -925,31 +919,32 @@ bool AbstractState::execute(unsigned indexInBlock) Edge child1 = m_graph.varArgChild(node, 0); Edge child2 = m_graph.varArgChild(node, 1); Edge child3 = m_graph.varArgChild(node, 2); - switch (modeForPut(node.arrayMode())) { + switch (node.arrayMode().modeForPut().type()) { case Array::ForceExit: m_isValid = false; break; case Array::Generic: clobberWorld(node.codeOrigin, indexInBlock); break; - case IN_BOUNDS_CONTIGUOUS_MODES: - case CONTIGUOUS_TO_TAIL_MODES: - case IN_BOUNDS_ARRAY_STORAGE_MODES: + case Array::Contiguous: + case Array::ArrayStorage: + forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); + if (node.arrayMode().isOutOfBounds()) + clobberWorld(node.codeOrigin, indexInBlock); break; - case OUT_OF_BOUNDS_CONTIGUOUS_MODES: - case ARRAY_STORAGE_TO_HOLE_MODES: - case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: - case SLOW_PUT_ARRAY_STORAGE_MODES: - case ALL_EFFECTFUL_MODES: + case Array::SlowPutArrayStorage: forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); - clobberWorld(node.codeOrigin, indexInBlock); + if (node.arrayMode().mayStoreToHole()) + clobberWorld(node.codeOrigin, indexInBlock); break; case Array::Arguments: + forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); break; case Array::Int8Array: + forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -957,6 +952,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Int16Array: + forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -964,6 +960,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Int32Array: + forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -971,6 +968,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Uint8Array: + forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -978,6 +976,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Uint8ClampedArray: + forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -985,6 +984,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Uint16Array: + forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -992,6 +992,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Uint32Array: + forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -999,15 +1000,17 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Float32Array: + forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); forNode(child3).filter(SpecNumber); break; case Array::Float64Array: + forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); forNode(child3).filter(SpecNumber); break; default: - ASSERT_NOT_REACHED(); + CRASH(); break; } break; @@ -1324,7 +1327,8 @@ bool AbstractState::execute(unsigned indexInBlock) // 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)) + if (value.m_futurePossibleStructure.isSubsetOf(set) + || value.m_currentKnownStructure.isSubsetOf(set)) m_foundConstants = true; node.setCanExit( !value.m_currentKnownStructure.isSubsetOf(set) @@ -1365,23 +1369,24 @@ bool AbstractState::execute(unsigned indexInBlock) case GetButterfly: case AllocatePropertyStorage: case ReallocatePropertyStorage: - node.setCanExit(false); + node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type)); forNode(node.child1()).filter(SpecCell); forNode(nodeIndex).clear(); // The result is not a JS value. break; case CheckArray: { - if (modeAlreadyChecked(forNode(node.child1()), node.arrayMode())) { + if (node.arrayMode().alreadyChecked(forNode(node.child1()))) { m_foundConstants = true; node.setCanExit(false); break; } node.setCanExit(true); // Lies, but this is followed by operations (like GetByVal) that always exit, so there is no point in us trying to be clever here. - switch (node.arrayMode()) { + switch (node.arrayMode().type()) { case Array::String: forNode(node.child1()).filter(SpecString); break; - case ALL_CONTIGUOUS_MODES: - case ALL_ARRAY_STORAGE_MODES: + case Array::Contiguous: + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: // This doesn't filter anything meaningful right now. We may want to add // CFA tracking of array mode speculations, but we don't have that, yet. forNode(node.child1()).filter(SpecCell); @@ -1420,33 +1425,40 @@ bool AbstractState::execute(unsigned indexInBlock) ASSERT_NOT_REACHED(); break; } - forNode(node.child1()).filterArrayModes(arrayModesFor(node.arrayMode())); + forNode(node.child1()).filterArrayModes(node.arrayMode().arrayModesThatPassFiltering()); + m_haveStructures = true; break; } case Arrayify: { - if (modeAlreadyChecked(forNode(node.child1()), node.arrayMode())) { + if (node.arrayMode().alreadyChecked(forNode(node.child1()))) { m_foundConstants = true; node.setCanExit(false); break; } - switch (node.arrayMode()) { - case ALL_EFFECTFUL_MODES: - node.setCanExit(true); - forNode(node.child1()).filter(SpecCell); - if (node.child2()) - forNode(node.child2()).filter(SpecInt32); - forNode(nodeIndex).clear(); - clobberStructures(indexInBlock); - forNode(node.child1()).filterArrayModes(arrayModesFor(node.arrayMode())); - break; - default: - CRASH(); - break; - } + ASSERT(node.arrayMode().conversion() == Array::Convert); + node.setCanExit(true); + forNode(node.child1()).filter(SpecCell); + if (node.child2()) + forNode(node.child2()).filter(SpecInt32); + clobberStructures(indexInBlock); + forNode(node.child1()).filterArrayModes(node.arrayMode().arrayModesThatPassFiltering()); + m_haveStructures = true; + break; + } + case ArrayifyToStructure: { + AbstractValue& value = forNode(node.child1()); + StructureSet set = node.structure(); + if (value.m_futurePossibleStructure.isSubsetOf(set) + || value.m_currentKnownStructure.isSubsetOf(set)) + m_foundConstants = true; + node.setCanExit(true); + clobberStructures(indexInBlock); + value.filter(set); + m_haveStructures = true; break; } case GetIndexedPropertyStorage: { - switch (node.arrayMode()) { + switch (node.arrayMode().type()) { case Array::String: // Strings are weird - we may spec fail if the string was a rope. That is of course // stupid, and we should fix that, but for now let's at least be honest about it. @@ -1460,13 +1472,13 @@ bool AbstractState::execute(unsigned indexInBlock) break; } case GetByOffset: - node.setCanExit(false); + node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type)); forNode(node.child1()).filter(SpecCell); forNode(nodeIndex).makeTop(); break; case PutByOffset: - node.setCanExit(false); + node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type)); forNode(node.child1()).filter(SpecCell); break; diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h index 5382cd3ad..c198b5e52 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractValue.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.h @@ -458,29 +458,11 @@ struct AbstractValue { private: void clobberArrayModes() { - if (m_arrayModes == ALL_ARRAY_MODES) - return; - - if (LIKELY(m_arrayModes & asArrayModes(NonArray))) - m_arrayModes = ALL_ARRAY_MODES; - else - clobberArrayModesSlow(); + // FIXME: We could make this try to predict the set of array modes that this object + // could have in the future. For now, just do the simple thing. + m_arrayModes = ALL_ARRAY_MODES; } - void clobberArrayModesSlow() - { - if (m_arrayModes & asArrayModes(ArrayClass)) - m_arrayModes = ALL_ARRAY_MODES; - else if (m_arrayModes & asArrayModes(NonArrayWithContiguous)) - m_arrayModes |= asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage); - else if (m_arrayModes & asArrayModes(ArrayWithContiguous)) - m_arrayModes |= asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage); - else if (m_arrayModes & asArrayModes(NonArrayWithArrayStorage)) - m_arrayModes |= asArrayModes(NonArrayWithSlowPutArrayStorage); - else if (m_arrayModes & asArrayModes(ArrayWithArrayStorage)) - m_arrayModes |= asArrayModes(ArrayWithArrayStorage); - } - void setFuturePossibleStructure(Structure* structure) { if (structure->transitionWatchpointSetIsStillValid()) diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp index bb61a59e6..00b1109f6 100644 --- a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp @@ -273,7 +273,7 @@ public: } case GetByVal: { - if (node.arrayMode() != Array::Arguments) { + if (node.arrayMode().type() != Array::Arguments) { observeBadArgumentsUses(node); break; } @@ -287,7 +287,7 @@ public: } case GetArrayLength: { - if (node.arrayMode() != Array::Arguments) { + if (node.arrayMode().type() != Array::Arguments) { observeBadArgumentsUses(node); break; } @@ -476,7 +476,7 @@ public: } case GetByVal: { - if (node.arrayMode() != Array::Arguments) + if (node.arrayMode().type() != Array::Arguments) break; // This can be simplified to GetMyArgumentByVal if we know that @@ -499,7 +499,7 @@ public: } case GetArrayLength: { - if (node.arrayMode() != Array::Arguments) + if (node.arrayMode().type() != Array::Arguments) break; if (!isOKToOptimize(m_graph[node.child1()])) diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp index a3aafde01..699902a16 100644 --- a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp +++ b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp @@ -32,126 +32,126 @@ namespace JSC { namespace DFG { -Array::Mode fromObserved(ArrayProfile* profile, Array::Action action, bool makeSafe) +ArrayMode ArrayMode::fromObserved(ArrayProfile* profile, Array::Action action, bool makeSafe) { switch (profile->observedArrayModes()) { case 0: - return Array::Unprofiled; + return ArrayMode(Array::Unprofiled); case asArrayModes(NonArray): if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) - return Array::ToContiguous; // FIXME: we don't know whether to go to contiguous or array storage. We're making a static guess here. In future we should use exit profiling for this. - return Array::SelectUsingPredictions; + return ArrayMode(Array::Contiguous, Array::NonArray, Array::OutOfBounds, Array::Convert); // FIXME: we don't know whether to go to contiguous or array storage. We're making a static guess here. In future we should use exit profiling for this. + return ArrayMode(Array::SelectUsingPredictions); case asArrayModes(NonArrayWithContiguous): - return makeSafe ? Array::ContiguousOutOfBounds : (profile->mayStoreToHole() ? Array::ContiguousToTail : Array::Contiguous); + return ArrayMode(Array::Contiguous, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(ArrayWithContiguous): - return makeSafe ? Array::ArrayWithContiguousOutOfBounds : (profile->mayStoreToHole() ? Array::ArrayWithContiguousToTail : Array::ArrayWithContiguous); + return ArrayMode(Array::Contiguous, Array::Array, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous): - return makeSafe ? Array::PossiblyArrayWithContiguousOutOfBounds : (profile->mayStoreToHole() ? Array::PossiblyArrayWithContiguousToTail : Array::PossiblyArrayWithContiguous); + return ArrayMode(Array::Contiguous, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(NonArrayWithArrayStorage): - return makeSafe ? Array::ArrayStorageOutOfBounds : (profile->mayStoreToHole() ? Array::ArrayStorageToHole : Array::ArrayStorage); + return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(NonArrayWithSlowPutArrayStorage): case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage): - return Array::SlowPutArrayStorage; + return ArrayMode(Array::SlowPutArrayStorage, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(ArrayWithArrayStorage): - return makeSafe ? Array::ArrayWithArrayStorageOutOfBounds : (profile->mayStoreToHole() ? Array::ArrayWithArrayStorageToHole : Array::ArrayWithArrayStorage); + return ArrayMode(Array::ArrayStorage, Array::Array, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(ArrayWithSlowPutArrayStorage): case asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage): - return Array::ArrayWithSlowPutArrayStorage; + return ArrayMode(Array::SlowPutArrayStorage, Array::Array, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage): - return makeSafe ? Array::PossiblyArrayWithArrayStorageOutOfBounds : (profile->mayStoreToHole() ? Array::PossiblyArrayWithArrayStorageToHole : Array::PossiblyArrayWithArrayStorage); + return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage): case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage): - return Array::PossiblyArrayWithSlowPutArrayStorage; + return ArrayMode(Array::SlowPutArrayStorage, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage): - return Array::ToArrayStorage; + return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::Convert).withProfile(profile, makeSafe); case asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage): - return Array::ArrayToArrayStorage; + return ArrayMode(Array::ArrayStorage, Array::Array, Array::Convert).withProfile(profile, makeSafe); case asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage): - return Array::PossiblyArrayToArrayStorage; + return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::Convert).withProfile(profile, makeSafe); case asArrayModes(NonArray) | asArrayModes(NonArrayWithContiguous): if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) - return Array::ToContiguous; - return Array::SelectUsingPredictions; + return ArrayMode(Array::Contiguous, Array::NonArray, Array::OutOfBounds, Array::Convert); + return ArrayMode(Array::SelectUsingPredictions); case asArrayModes(NonArray) | asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage): case asArrayModes(NonArray) | asArrayModes(NonArrayWithArrayStorage): if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) - return Array::ToArrayStorage; - return Array::SelectUsingPredictions; + return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::OutOfBounds, Array::Convert); + return ArrayMode(Array::SelectUsingPredictions); case asArrayModes(NonArray) | asArrayModes(NonArrayWithSlowPutArrayStorage): case asArrayModes(NonArray) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage): if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) - return Array::ToSlowPutArrayStorage; - return Array::SelectUsingPredictions; + return ArrayMode(Array::SlowPutArrayStorage, Array::NonArray, Array::OutOfBounds, Array::Convert); + return ArrayMode(Array::SelectUsingPredictions); default: // We know that this is possibly a kind of array for which, though there is no // useful data in the array profile, we may be able to extract useful data from // the value profiles of the inputs. Hence, we leave it as undecided, and let // the predictions propagator decide later. - return Array::SelectUsingPredictions; + return ArrayMode(Array::SelectUsingPredictions); } } -Array::Mode refineArrayMode(Array::Mode arrayMode, SpeculatedType base, SpeculatedType index) +ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index) const { if (!base || !index) { // It can be that we had a legitimate arrayMode but no incoming predictions. That'll // happen if we inlined code based on, say, a global variable watchpoint, but later // realized that the callsite could not have possibly executed. It may be worthwhile // to fix that, but for now I'm leaving it as-is. - return Array::ForceExit; + return ArrayMode(Array::ForceExit); } if (!isInt32Speculation(index) || !isCellSpeculation(base)) - return Array::Generic; + return ArrayMode(Array::Generic); - if (arrayMode == Array::Unprofiled) { + if (type() == Array::Unprofiled) { // If the indexing type wasn't recorded in the array profile but the values are // base=cell property=int, then we know that this access didn't execute. - return Array::ForceExit; + return ArrayMode(Array::ForceExit); } - if (arrayMode != Array::SelectUsingPredictions) - return arrayMode; + if (type() != Array::SelectUsingPredictions) + return *this; if (isStringSpeculation(base)) - return Array::String; + return ArrayMode(Array::String); if (isArgumentsSpeculation(base)) - return Array::Arguments; + return ArrayMode(Array::Arguments); if (isInt8ArraySpeculation(base)) - return Array::Int8Array; + return ArrayMode(Array::Int8Array); if (isInt16ArraySpeculation(base)) - return Array::Int16Array; + return ArrayMode(Array::Int16Array); if (isInt32ArraySpeculation(base)) - return Array::Int32Array; + return ArrayMode(Array::Int32Array); if (isUint8ArraySpeculation(base)) - return Array::Uint8Array; + return ArrayMode(Array::Uint8Array); if (isUint8ClampedArraySpeculation(base)) - return Array::Uint8ClampedArray; + return ArrayMode(Array::Uint8ClampedArray); if (isUint16ArraySpeculation(base)) - return Array::Uint16Array; + return ArrayMode(Array::Uint16Array); if (isUint32ArraySpeculation(base)) - return Array::Uint32Array; + return ArrayMode(Array::Uint32Array); if (isFloat32ArraySpeculation(base)) - return Array::Float32Array; + return ArrayMode(Array::Float32Array); if (isFloat64ArraySpeculation(base)) - return Array::Float64Array; + return ArrayMode(Array::Float64Array); - return Array::Generic; + return ArrayMode(Array::Generic); } -bool modeAlreadyChecked(AbstractValue& value, Array::Mode arrayMode) +bool ArrayMode::alreadyChecked(AbstractValue& value) const { - switch (arrayMode) { + switch (type()) { case Array::Generic: return true; @@ -159,109 +159,89 @@ bool modeAlreadyChecked(AbstractValue& value, Array::Mode arrayMode) return false; case Array::String: - return isStringSpeculation(value.m_type); + return speculationChecked(value.m_type, SpecString); case Array::Contiguous: - case Array::ContiguousToTail: - case Array::ContiguousOutOfBounds: - case Array::PossiblyArrayWithContiguous: - case Array::PossiblyArrayWithContiguousToTail: - case Array::PossiblyArrayWithContiguousOutOfBounds: - case Array::ToContiguous: + if (isJSArray()) { + if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithContiguous))) + return true; + return value.m_currentKnownStructure.hasSingleton() + && hasContiguous(value.m_currentKnownStructure.singleton()->indexingType()) + && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); + } if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous))) return true; return value.m_currentKnownStructure.hasSingleton() && hasContiguous(value.m_currentKnownStructure.singleton()->indexingType()); - case Array::ArrayWithContiguous: - case Array::ArrayWithContiguousToTail: - case Array::ArrayWithContiguousOutOfBounds: - if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithContiguous))) - return true; - return value.m_currentKnownStructure.hasSingleton() - && hasContiguous(value.m_currentKnownStructure.singleton()->indexingType()) - && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); - case Array::ArrayStorage: - case Array::ArrayStorageToHole: - case Array::ArrayStorageOutOfBounds: - case Array::PossiblyArrayWithArrayStorage: - case Array::PossiblyArrayWithArrayStorageToHole: - case Array::PossiblyArrayWithArrayStorageOutOfBounds: - case Array::ToArrayStorage: - case Array::PossiblyArrayToArrayStorage: + if (isJSArray()) { + if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage))) + return true; + return value.m_currentKnownStructure.hasSingleton() + && hasFastArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()) + && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); + } if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage))) return true; return value.m_currentKnownStructure.hasSingleton() && hasFastArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()); case Array::SlowPutArrayStorage: - case Array::PossiblyArrayWithSlowPutArrayStorage: - case Array::ToSlowPutArrayStorage: + if (isJSArray()) { + if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage))) + return true; + return value.m_currentKnownStructure.hasSingleton() + && hasArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()) + && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); + } if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage))) return true; return value.m_currentKnownStructure.hasSingleton() && hasArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()); - case Array::ArrayWithArrayStorage: - case Array::ArrayWithArrayStorageToHole: - case Array::ArrayWithArrayStorageOutOfBounds: - case Array::ArrayToArrayStorage: - if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage))) - return true; - return value.m_currentKnownStructure.hasSingleton() - && hasFastArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()) - && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); - - case Array::ArrayWithSlowPutArrayStorage: - if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage))) - return true; - return value.m_currentKnownStructure.hasSingleton() - && hasArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()) - && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); - case Array::Arguments: - return isArgumentsSpeculation(value.m_type); + return speculationChecked(value.m_type, SpecArguments); case Array::Int8Array: - return isInt8ArraySpeculation(value.m_type); + return speculationChecked(value.m_type, SpecInt8Array); case Array::Int16Array: - return isInt16ArraySpeculation(value.m_type); + return speculationChecked(value.m_type, SpecInt16Array); case Array::Int32Array: - return isInt32ArraySpeculation(value.m_type); + return speculationChecked(value.m_type, SpecInt32Array); case Array::Uint8Array: - return isUint8ArraySpeculation(value.m_type); + return speculationChecked(value.m_type, SpecUint8Array); case Array::Uint8ClampedArray: - return isUint8ClampedArraySpeculation(value.m_type); + return speculationChecked(value.m_type, SpecUint8ClampedArray); case Array::Uint16Array: - return isUint16ArraySpeculation(value.m_type); + return speculationChecked(value.m_type, SpecUint16Array); case Array::Uint32Array: - return isUint32ArraySpeculation(value.m_type); + return speculationChecked(value.m_type, SpecUint32Array); case Array::Float32Array: - return isFloat32ArraySpeculation(value.m_type); + return speculationChecked(value.m_type, SpecFloat32Array); case Array::Float64Array: - return isFloat64ArraySpeculation(value.m_type); + return speculationChecked(value.m_type, SpecFloat64Array); case Array::SelectUsingPredictions: case Array::Unprofiled: break; } - ASSERT_NOT_REACHED(); + CRASH(); return false; } -const char* modeToString(Array::Mode mode) +const char* arrayTypeToString(Array::Type type) { - switch (mode) { + switch (type) { case Array::SelectUsingPredictions: return "SelectUsingPredictions"; case Array::Unprofiled: @@ -274,56 +254,10 @@ const char* modeToString(Array::Mode mode) return "String"; case Array::Contiguous: return "Contiguous"; - case Array::ContiguousToTail: - return "ContiguousToTail"; - case Array::ContiguousOutOfBounds: - return "ContiguousOutOfBounds"; - case Array::ArrayWithContiguous: - return "ArrayWithContiguous"; - case Array::ArrayWithContiguousToTail: - return "ArrayWithContiguousToTail"; - case Array::ArrayWithContiguousOutOfBounds: - return "ArrayWithContiguousOutOfBounds"; - case Array::PossiblyArrayWithContiguous: - return "PossiblyArrayWithContiguous"; - case Array::PossiblyArrayWithContiguousToTail: - return "PossiblyArrayWithContiguousToTail"; - case Array::PossiblyArrayWithContiguousOutOfBounds: - return "PossiblyArrayWithContiguousOutOfBounds"; case Array::ArrayStorage: return "ArrayStorage"; - case Array::ArrayStorageToHole: - return "ArrayStorageToHole"; case Array::SlowPutArrayStorage: return "SlowPutArrayStorage"; - case Array::ArrayStorageOutOfBounds: - return "ArrayStorageOutOfBounds"; - case Array::ArrayWithArrayStorage: - return "ArrayWithArrayStorage"; - case Array::ArrayWithArrayStorageToHole: - return "ArrayWithArrayStorageToHole"; - case Array::ArrayWithSlowPutArrayStorage: - return "ArrayWithSlowPutArrayStorage"; - case Array::ArrayWithArrayStorageOutOfBounds: - return "ArrayWithArrayStorageOutOfBounds"; - case Array::PossiblyArrayWithArrayStorage: - return "PossiblyArrayWithArrayStorage"; - case Array::PossiblyArrayWithArrayStorageToHole: - return "PossiblyArrayWithArrayStorageToHole"; - case Array::PossiblyArrayWithSlowPutArrayStorage: - return "PossiblyArrayWithSlowPutArrayStorage"; - case Array::PossiblyArrayWithArrayStorageOutOfBounds: - return "PossiblyArrayWithArrayStorageOutOfBounds"; - case Array::ToContiguous: - return "ToContiguous"; - case Array::ToArrayStorage: - return "ToArrayStorage"; - case Array::ToSlowPutArrayStorage: - return "ToSlowPutArrayStorage"; - case Array::ArrayToArrayStorage: - return "ArrayToArrayStorage"; - case Array::PossiblyArrayToArrayStorage: - return "PossiblyArrayToArrayStorage"; case Array::Arguments: return "Arguments"; case Array::Int8Array: @@ -353,6 +287,55 @@ const char* modeToString(Array::Mode mode) } } +const char* arrayClassToString(Array::Class arrayClass) +{ + switch (arrayClass) { + case Array::Array: + return "Array"; + case Array::OriginalArray: + return "OriginalArray"; + case Array::NonArray: + return "NonArray"; + case Array::PossiblyArray: + return "PossiblyArray"; + default: + return "Unknown!"; + } +} + +const char* arraySpeculationToString(Array::Speculation speculation) +{ + switch (speculation) { + case Array::InBounds: + return "InBounds"; + case Array::ToHole: + return "ToHole"; + case Array::OutOfBounds: + return "OutOfBounds"; + default: + return "Unknown!"; + } +} + +const char* arrayConversionToString(Array::Conversion conversion) +{ + switch (conversion) { + case Array::AsIs: + return "AsIs"; + case Array::Convert: + return "Convert"; + default: + return "Unknown!"; + } +} + +const char* ArrayMode::toString() const +{ + static char buffer[256]; + snprintf(buffer, sizeof(buffer), "%s%s%s%s", arrayTypeToString(type()), arrayClassToString(arrayClass()), arraySpeculationToString(speculation()), arrayConversionToString(conversion())); + return buffer; +} + } } // namespace JSC::DFG #endif // ENABLE(DFG_JIT) diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.h b/Source/JavaScriptCore/dfg/DFGArrayMode.h index a1cd74114..615965c92 100644 --- a/Source/JavaScriptCore/dfg/DFGArrayMode.h +++ b/Source/JavaScriptCore/dfg/DFGArrayMode.h @@ -46,42 +46,16 @@ enum Action { Write }; -enum Mode { +enum Type { SelectUsingPredictions, // Implies that we need predictions to decide. We will never get to the backend in this mode. Unprofiled, // Implies that array profiling didn't see anything. But that could be because the operands didn't comply with basic type assumptions (base is cell, property is int). This either becomes Generic or ForceExit depending on value profiling. ForceExit, // Implies that we have no idea how to execute this operation, so we should just give up. Generic, String, - // Modes of conventional indexed storage where the check is non side-effecting. Contiguous, - ContiguousToTail, - ContiguousOutOfBounds, - ArrayWithContiguous, - ArrayWithContiguousToTail, - ArrayWithContiguousOutOfBounds, - PossiblyArrayWithContiguous, - PossiblyArrayWithContiguousToTail, - PossiblyArrayWithContiguousOutOfBounds, ArrayStorage, - ArrayStorageToHole, SlowPutArrayStorage, - ArrayStorageOutOfBounds, - ArrayWithArrayStorage, - ArrayWithArrayStorageToHole, - ArrayWithSlowPutArrayStorage, - ArrayWithArrayStorageOutOfBounds, - PossiblyArrayWithArrayStorage, - PossiblyArrayWithArrayStorageToHole, - PossiblyArrayWithSlowPutArrayStorage, - PossiblyArrayWithArrayStorageOutOfBounds, - - // Modes of conventional indexed storage where the check is side-effecting. - ToContiguous, - ToArrayStorage, - ArrayToArrayStorage, - PossiblyArrayToArrayStorage, - ToSlowPutArrayStorage, Arguments, Int8Array, @@ -94,304 +68,311 @@ enum Mode { Float32Array, Float64Array }; -} // namespace Array - -// Helpers for 'case' statements. For example, saying "case AllArrayStorageModes:" -// is the same as having multiple case statements listing off all of the modes that -// have the word "ArrayStorage" in them. - -// First: helpers for non-side-effecting checks. -#define NON_ARRAY_CONTIGUOUS_MODES \ - Array::Contiguous: \ - case Array::ContiguousToTail: \ - case Array::ContiguousOutOfBounds: \ - case Array::PossiblyArrayWithContiguous: \ - case Array::PossiblyArrayWithContiguousToTail: \ - case Array::PossiblyArrayWithContiguousOutOfBounds -#define ARRAY_WITH_CONTIGUOUS_MODES \ - Array::ArrayWithContiguous: \ - case Array::ArrayWithContiguousToTail: \ - case Array::ArrayWithContiguousOutOfBounds -#define ALL_CONTIGUOUS_MODES \ - NON_ARRAY_CONTIGUOUS_MODES: \ - case ARRAY_WITH_CONTIGUOUS_MODES -#define IN_BOUNDS_CONTIGUOUS_MODES \ - Array::Contiguous: \ - case Array::ArrayWithContiguous: \ - case Array::PossiblyArrayWithContiguous -#define CONTIGUOUS_TO_TAIL_MODES \ - Array::ContiguousToTail: \ - case Array::ArrayWithContiguousToTail: \ - case Array::PossiblyArrayWithContiguousToTail -#define OUT_OF_BOUNDS_CONTIGUOUS_MODES \ - Array::ContiguousOutOfBounds: \ - case Array::ArrayWithContiguousOutOfBounds: \ - case Array::PossiblyArrayWithContiguousOutOfBounds -#define NON_ARRAY_ARRAY_STORAGE_MODES \ - Array::ArrayStorage: \ - case Array::ArrayStorageToHole: \ - case Array::SlowPutArrayStorage: \ - case Array::ArrayStorageOutOfBounds: \ - case Array::PossiblyArrayWithArrayStorage: \ - case Array::PossiblyArrayWithArrayStorageToHole: \ - case Array::PossiblyArrayWithSlowPutArrayStorage: \ - case Array::PossiblyArrayWithArrayStorageOutOfBounds -#define ARRAY_WITH_ARRAY_STORAGE_MODES \ - Array::ArrayWithArrayStorage: \ - case Array::ArrayWithArrayStorageToHole: \ - case Array::ArrayWithSlowPutArrayStorage: \ - case Array::ArrayWithArrayStorageOutOfBounds -#define ALL_ARRAY_STORAGE_MODES \ - NON_ARRAY_ARRAY_STORAGE_MODES: \ - case ARRAY_WITH_ARRAY_STORAGE_MODES -#define IN_BOUNDS_ARRAY_STORAGE_MODES \ - Array::ArrayStorage: \ - case Array::ArrayWithArrayStorage: \ - case Array::PossiblyArrayWithArrayStorage -#define ARRAY_STORAGE_TO_HOLE_MODES \ - Array::ArrayStorageToHole: \ - case Array::ArrayWithArrayStorageToHole: \ - case Array::PossiblyArrayWithArrayStorageToHole -#define SLOW_PUT_ARRAY_STORAGE_MODES \ - Array::SlowPutArrayStorage: \ - case Array::ArrayWithSlowPutArrayStorage: \ - case Array::PossiblyArrayWithSlowPutArrayStorage -#define OUT_OF_BOUNDS_ARRAY_STORAGE_MODES \ - Array::ArrayStorageOutOfBounds: \ - case Array::ArrayWithArrayStorageOutOfBounds: \ - case Array::PossiblyArrayWithArrayStorageOutOfBounds -// Next: helpers for side-effecting checks. -#define NON_ARRAY_EFFECTFUL_MODES \ - Array::ToContiguous: \ - case Array::ToArrayStorage: \ - case Array::ToSlowPutArrayStorage: \ - case Array::PossiblyArrayToArrayStorage -#define ARRAY_EFFECTFUL_MODES \ - Array::ArrayToArrayStorage -#define ALL_EFFECTFUL_CONTIGUOUS_MODES \ - Array::ToContiguous -#define ALL_EFFECTFUL_ARRAY_STORAGE_MODES \ - Array::ToArrayStorage: \ - case Array::ToSlowPutArrayStorage: \ - case Array::ArrayToArrayStorage: \ - case Array::PossiblyArrayToArrayStorage -#define SLOW_PUT_EFFECTFUL_ARRAY_STORAGE_MODES \ - Array::ToSlowPutArrayStorage -#define ALL_EFFECTFUL_MODES \ - ALL_EFFECTFUL_CONTIGUOUS_MODES: \ - case ALL_EFFECTFUL_ARRAY_STORAGE_MODES - -Array::Mode fromObserved(ArrayProfile*, Array::Action, bool makeSafe); - -Array::Mode refineArrayMode(Array::Mode, SpeculatedType base, SpeculatedType index); +enum Class { + NonArray, // Definitely some object that is not a JSArray. + Array, // Definitely a JSArray, and may or may not have custom properties or have undergone some other bizarre transitions. + OriginalArray, // Definitely a JSArray, and still has one of the primordial JSArray structures for the global object that this code block (possibly inlined code block) belongs to. + PossiblyArray // Some object that may or may not be a JSArray. +}; -bool modeAlreadyChecked(AbstractValue&, Array::Mode); +enum Speculation { + InBounds, + ToHole, + OutOfBounds +}; -const char* modeToString(Array::Mode); +enum Conversion { + AsIs, + Convert +}; +} // namespace Array -inline bool modeUsesButterfly(Array::Mode arrayMode) -{ - switch (arrayMode) { - case ALL_CONTIGUOUS_MODES: - case ALL_ARRAY_STORAGE_MODES: - case ALL_EFFECTFUL_MODES: - return true; - default: - return false; +const char* arrayTypeToString(Array::Type); +const char* arrayClassToString(Array::Class); +const char* arraySpeculationToString(Array::Speculation); +const char* arrayConversionToString(Array::Conversion); + +class ArrayMode { +public: + ArrayMode() + { + u.asBytes.type = Array::SelectUsingPredictions; + u.asBytes.arrayClass = Array::NonArray; + u.asBytes.speculation = Array::InBounds; + u.asBytes.conversion = Array::AsIs; } -} - -inline bool modeIsJSArray(Array::Mode arrayMode) -{ - switch (arrayMode) { - case ARRAY_WITH_CONTIGUOUS_MODES: - case ARRAY_WITH_ARRAY_STORAGE_MODES: - case ARRAY_EFFECTFUL_MODES: - return true; - default: - return false; + + explicit ArrayMode(Array::Type type) + { + u.asBytes.type = type; + u.asBytes.arrayClass = Array::NonArray; + u.asBytes.speculation = Array::InBounds; + u.asBytes.conversion = Array::AsIs; } -} - -inline bool isInBoundsAccess(Array::Mode arrayMode) -{ - switch (arrayMode) { - case IN_BOUNDS_CONTIGUOUS_MODES: - case CONTIGUOUS_TO_TAIL_MODES: - case ARRAY_STORAGE_TO_HOLE_MODES: - case IN_BOUNDS_ARRAY_STORAGE_MODES: - return true; - default: - return false; + + ArrayMode(Array::Type type, Array::Class arrayClass, Array::Speculation speculation, Array::Conversion conversion) + { + u.asBytes.type = type; + u.asBytes.arrayClass = arrayClass; + u.asBytes.speculation = speculation; + u.asBytes.conversion = conversion; } -} - -inline bool isSlowPutAccess(Array::Mode arrayMode) -{ - switch (arrayMode) { - case SLOW_PUT_ARRAY_STORAGE_MODES: - case SLOW_PUT_EFFECTFUL_ARRAY_STORAGE_MODES: - return true; - default: - return false; + + ArrayMode(Array::Type type, Array::Class arrayClass, Array::Conversion conversion) + { + u.asBytes.type = type; + u.asBytes.arrayClass = arrayClass; + u.asBytes.speculation = Array::InBounds; + u.asBytes.conversion = conversion; } -} - -inline bool mayStoreToTail(Array::Mode arrayMode) -{ - switch (arrayMode) { - case CONTIGUOUS_TO_TAIL_MODES: - case OUT_OF_BOUNDS_CONTIGUOUS_MODES: - case ALL_EFFECTFUL_CONTIGUOUS_MODES: - return true; - default: - return false; + + Array::Type type() const { return static_cast<Array::Type>(u.asBytes.type); } + Array::Class arrayClass() const { return static_cast<Array::Class>(u.asBytes.arrayClass); } + Array::Speculation speculation() const { return static_cast<Array::Speculation>(u.asBytes.speculation); } + Array::Conversion conversion() const { return static_cast<Array::Conversion>(u.asBytes.conversion); } + + unsigned asWord() const { return u.asWord; } + + static ArrayMode fromWord(unsigned word) + { + return ArrayMode(word); } -} - -inline bool mayStoreToHole(Array::Mode arrayMode) -{ - switch (arrayMode) { - case ARRAY_STORAGE_TO_HOLE_MODES: - case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: - case SLOW_PUT_ARRAY_STORAGE_MODES: - case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: - return true; - default: - return false; + + static ArrayMode fromObserved(ArrayProfile*, Array::Action, bool makeSafe); + + ArrayMode withSpeculation(Array::Speculation speculation) const + { + return ArrayMode(type(), arrayClass(), speculation, conversion()); } -} - -inline bool canCSEStorage(Array::Mode arrayMode) -{ - switch (arrayMode) { - case Array::SelectUsingPredictions: - case Array::Unprofiled: - case Array::ForceExit: - case Array::Generic: - case Array::Arguments: - return false; - default: - return true; + + ArrayMode withProfile(ArrayProfile* profile, bool makeSafe) const + { + Array::Speculation mySpeculation; + Array::Class myArrayClass; + + if (makeSafe) + mySpeculation = Array::OutOfBounds; + else if (profile->mayStoreToHole()) + mySpeculation = Array::ToHole; + else + mySpeculation = Array::InBounds; + + if (isJSArray()) { + if (profile->usesOriginalArrayStructures()) + myArrayClass = Array::OriginalArray; + else + myArrayClass = Array::Array; + } else + myArrayClass = arrayClass(); + + return ArrayMode(type(), myArrayClass, mySpeculation, conversion()); } -} - -inline bool lengthNeedsStorage(Array::Mode arrayMode) -{ - return modeIsJSArray(arrayMode); -} - -inline Array::Mode modeForPut(Array::Mode arrayMode) -{ - switch (arrayMode) { - case Array::String: - return Array::Generic; + + ArrayMode refine(SpeculatedType base, SpeculatedType index) const; + + bool alreadyChecked(AbstractValue&) const; + + const char* toString() const; + + bool usesButterfly() const + { + switch (type()) { + case Array::Contiguous: + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: + return true; + default: + return false; + } + } + + bool isJSArray() const + { + switch (arrayClass()) { + case Array::Array: + case Array::OriginalArray: + return true; + default: + return false; + } + } + + bool isJSArrayWithOriginalStructure() const + { + return arrayClass() == Array::OriginalArray; + } + + bool isInBounds() const + { + return speculation() == Array::InBounds; + } + + bool mayStoreToHole() const + { + return !isInBounds(); + } + + bool isOutOfBounds() const + { + return speculation() == Array::OutOfBounds; + } + + bool isSlowPut() const + { + return type() == Array::SlowPutArrayStorage; + } + + bool canCSEStorage() const + { + switch (type()) { + case Array::SelectUsingPredictions: + case Array::Unprofiled: + case Array::ForceExit: + case Array::Generic: + case Array::Arguments: + return false; + default: + return true; + } + } + + bool lengthNeedsStorage() const + { + return isJSArray(); + } + + ArrayMode modeForPut() const + { + switch (type()) { + case Array::String: + return ArrayMode(Array::Generic); #if USE(JSVALUE32_64) - case Array::Arguments: - return Array::Generic; + case Array::Arguments: + return ArrayMode(Array::Generic); #endif - default: - return arrayMode; + default: + return *this; + } } -} - -inline bool modeIsSpecific(Array::Mode mode) -{ - switch (mode) { - case Array::SelectUsingPredictions: - case Array::Unprofiled: - case Array::ForceExit: - case Array::Generic: - return false; - default: - return true; + + bool isSpecific() const + { + switch (type()) { + case Array::SelectUsingPredictions: + case Array::Unprofiled: + case Array::ForceExit: + case Array::Generic: + return false; + default: + return true; + } } -} - -inline bool modeSupportsLength(Array::Mode mode) -{ - switch (mode) { - case Array::SelectUsingPredictions: - case Array::Unprofiled: - case Array::ForceExit: - case Array::Generic: - case NON_ARRAY_CONTIGUOUS_MODES: - case NON_ARRAY_ARRAY_STORAGE_MODES: - case NON_ARRAY_EFFECTFUL_MODES: - return false; - default: - return true; + + bool supportsLength() const + { + switch (type()) { + case Array::SelectUsingPredictions: + case Array::Unprofiled: + case Array::ForceExit: + case Array::Generic: + return false; + case Array::Contiguous: + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: + return isJSArray(); + default: + return true; + } } -} - -inline bool benefitsFromStructureCheck(Array::Mode mode) -{ - switch (mode) { - case ALL_EFFECTFUL_MODES: - case Array::SelectUsingPredictions: - case Array::Unprofiled: - case Array::ForceExit: - case Array::Generic: - return false; - default: - return true; + + bool benefitsFromStructureCheck() const + { + switch (type()) { + case Array::SelectUsingPredictions: + case Array::Unprofiled: + case Array::ForceExit: + case Array::Generic: + return false; + default: + return conversion() == Array::AsIs; + } } -} + + bool doesConversion() const + { + return conversion() == Array::Convert; + } + + ArrayModes arrayModesThatPassFiltering() const + { + switch (type()) { + case Array::Generic: + return ALL_ARRAY_MODES; + case Array::Contiguous: + return arrayModesWithIndexingShape(ContiguousShape); + case Array::ArrayStorage: + return arrayModesWithIndexingShape(ArrayStorageShape); + case Array::SlowPutArrayStorage: + return arrayModesWithIndexingShape(SlowPutArrayStorageShape); + default: + return asArrayModes(NonArray); + } + } + + bool operator==(const ArrayMode& other) const + { + return type() == other.type() + && arrayClass() == other.arrayClass() + && speculation() == other.speculation() + && conversion() == other.conversion(); + } + + bool operator!=(const ArrayMode& other) const + { + return !(*this == other); + } +private: + explicit ArrayMode(unsigned word) + { + u.asWord = word; + } + + ArrayModes arrayModesWithIndexingShape(IndexingType shape) const + { + switch (arrayClass()) { + case Array::NonArray: + return asArrayModes(shape); + case Array::Array: + case Array::OriginalArray: + return asArrayModes(shape | IsArray); + case Array::PossiblyArray: + return asArrayModes(shape) | asArrayModes(shape | IsArray); + default: + // This is only necessary for C++ compilers that don't understand enums. + return 0; + } + } + + union { + struct { + uint8_t type; + uint8_t arrayClass; + uint8_t speculation; + uint8_t conversion; + } asBytes; + unsigned asWord; + } u; +}; -inline bool isEffectful(Array::Mode mode) +static inline bool canCSEStorage(const ArrayMode& arrayMode) { - switch (mode) { - case ALL_EFFECTFUL_MODES: - return true; - default: - return false; - } + return arrayMode.canCSEStorage(); } -// This returns the set of array modes that will pass filtering of a CheckArray or -// Arrayify with the given mode. -inline ArrayModes arrayModesFor(Array::Mode arrayMode) +static inline bool lengthNeedsStorage(const ArrayMode& arrayMode) { - switch (arrayMode) { - case Array::Generic: - return ALL_ARRAY_MODES; - case Array::Contiguous: - case Array::ContiguousToTail: - case Array::ContiguousOutOfBounds: - case Array::ToContiguous: - return asArrayModes(NonArrayWithContiguous); - case Array::PossiblyArrayWithContiguous: - case Array::PossiblyArrayWithContiguousToTail: - case Array::PossiblyArrayWithContiguousOutOfBounds: - return asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous); - case ARRAY_WITH_CONTIGUOUS_MODES: - return asArrayModes(ArrayWithContiguous); - case Array::ArrayStorage: - case Array::ArrayStorageToHole: - case Array::ArrayStorageOutOfBounds: - case Array::ToArrayStorage: - return asArrayModes(NonArrayWithArrayStorage); - case Array::ToSlowPutArrayStorage: - case Array::SlowPutArrayStorage: - return asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage); - case Array::PossiblyArrayWithArrayStorage: - case Array::PossiblyArrayWithArrayStorageToHole: - case Array::PossiblyArrayWithArrayStorageOutOfBounds: - case Array::PossiblyArrayToArrayStorage: - return asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage); - case Array::PossiblyArrayWithSlowPutArrayStorage: - return asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage); - case Array::ArrayWithArrayStorage: - case Array::ArrayWithArrayStorageToHole: - case Array::ArrayWithArrayStorageOutOfBounds: - case Array::ArrayToArrayStorage: - return asArrayModes(ArrayWithArrayStorage); - case Array::ArrayWithSlowPutArrayStorage: - return asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage); - default: - return asArrayModes(NonArray); - } + return arrayMode.lengthNeedsStorage(); } } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGAssemblyHelpers.h b/Source/JavaScriptCore/dfg/DFGAssemblyHelpers.h index 953a743ff..75b9c7072 100644 --- a/Source/JavaScriptCore/dfg/DFGAssemblyHelpers.h +++ b/Source/JavaScriptCore/dfg/DFGAssemblyHelpers.h @@ -320,6 +320,13 @@ public: return baselineCodeBlockForOriginAndBaselineCodeBlock(codeOrigin, baselineCodeBlock()); } + CodeBlock* baselineCodeBlockFor(InlineCallFrame* inlineCallFrame) + { + if (!inlineCallFrame) + return baselineCodeBlock(); + return baselineCodeBlockForInlineCallFrame(inlineCallFrame); + } + CodeBlock* baselineCodeBlock() { return m_baselineCodeBlock; diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeCache.h b/Source/JavaScriptCore/dfg/DFGByteCodeCache.h index 6b9056e54..e1837b041 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeCache.h +++ b/Source/JavaScriptCore/dfg/DFGByteCodeCache.h @@ -158,7 +158,7 @@ public: // Nope, so try to parse one. JSObject* exception; value.owned = true; - value.codeBlock = key.executable()->produceCodeBlockFor(scope, OptimizingCompilation, key.kind(), exception).leakPtr(); + value.codeBlock = key.executable()->produceCodeBlockFor(scope, key.kind(), exception).leakPtr(); } // Check if there is any reason to reject this from our cache. If so, then diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index 36d18d7b3..70aa2b637 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -35,7 +35,6 @@ #include "DFGByteCodeCache.h" #include "DFGCapabilities.h" #include "GetByIdStatus.h" -#include "MethodCallLinkStatus.h" #include "PutByIdStatus.h" #include "ResolveGlobalStatus.h" #include <wtf/HashMap.h> @@ -906,15 +905,15 @@ private: return getPrediction(m_graph.size(), m_currentProfilingIndex); } - Array::Mode getArrayMode(ArrayProfile* profile) + ArrayMode getArrayMode(ArrayProfile* profile) { - profile->computeUpdatedPrediction(); - return fromObserved(profile, Array::Read, false); + profile->computeUpdatedPrediction(m_inlineStackTop->m_codeBlock); + return ArrayMode::fromObserved(profile, Array::Read, false); } - Array::Mode getArrayModeAndEmitChecks(ArrayProfile* profile, Array::Action action, NodeIndex base) + ArrayMode getArrayModeAndEmitChecks(ArrayProfile* profile, Array::Action action, NodeIndex base) { - profile->computeUpdatedPrediction(); + profile->computeUpdatedPrediction(m_inlineStackTop->m_codeBlock); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) if (m_inlineStackTop->m_profiledBlock->numberOfRareCaseProfiles()) @@ -926,9 +925,9 @@ private: m_inlineStackTop->m_profiledBlock->couldTakeSlowCase(m_currentIndex) || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, OutOfBounds); - Array::Mode result = fromObserved(profile, action, makeSafe); + ArrayMode result = ArrayMode::fromObserved(profile, action, makeSafe); - if (profile->hasDefiniteStructure() && benefitsFromStructureCheck(result)) + if (profile->hasDefiniteStructure() && result.benefitsFromStructureCheck()) addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(profile->expectedStructure())), base); return result; @@ -1649,16 +1648,11 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins if (argumentCountIncludingThis != 2) return false; - Array::Mode arrayMode = getArrayMode(m_currentInstruction[5].u.arrayProfile); - switch (arrayMode) { - case Array::ArrayWithArrayStorageToHole: - ASSERT_NOT_REACHED(); - - case Array::ArrayWithContiguous: - case Array::ArrayWithContiguousOutOfBounds: - case Array::ArrayWithArrayStorage: - case Array::ArrayWithArrayStorageOutOfBounds: { - NodeIndex arrayPush = addToGraph(ArrayPush, OpInfo(arrayMode), OpInfo(prediction), get(registerOffset + argumentToOperand(0)), get(registerOffset + argumentToOperand(1))); + ArrayMode arrayMode = getArrayMode(m_currentInstruction[5].u.arrayProfile); + switch (arrayMode.type()) { + case Array::Contiguous: + case Array::ArrayStorage: { + NodeIndex arrayPush = addToGraph(ArrayPush, OpInfo(arrayMode.asWord()), OpInfo(prediction), get(registerOffset + argumentToOperand(0)), get(registerOffset + argumentToOperand(1))); if (usesResult) set(resultOperand, arrayPush); @@ -1674,16 +1668,11 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins if (argumentCountIncludingThis != 1) return false; - Array::Mode arrayMode = getArrayMode(m_currentInstruction[5].u.arrayProfile); - switch (arrayMode) { - case Array::ArrayWithArrayStorageToHole: - ASSERT_NOT_REACHED(); - - case Array::ArrayWithContiguous: - case Array::ArrayWithContiguousOutOfBounds: - case Array::ArrayWithArrayStorage: - case Array::ArrayWithArrayStorageOutOfBounds: { - NodeIndex arrayPop = addToGraph(ArrayPop, OpInfo(arrayMode), OpInfo(prediction), get(registerOffset + argumentToOperand(0))); + ArrayMode arrayMode = getArrayMode(m_currentInstruction[5].u.arrayProfile); + switch (arrayMode.type()) { + case Array::Contiguous: + case Array::ArrayStorage: { + NodeIndex arrayPop = addToGraph(ArrayPop, OpInfo(arrayMode.asWord()), OpInfo(prediction), get(registerOffset + argumentToOperand(0))); if (usesResult) set(resultOperand, arrayPop); return true; @@ -1699,9 +1688,6 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins return false; int thisOperand = registerOffset + argumentToOperand(0); - if (!(m_graph[get(thisOperand)].prediction() & SpecString)) - return false; - int indexOperand = registerOffset + argumentToOperand(1); NodeIndex charCode = addToGraph(StringCharCodeAt, OpInfo(Array::String), get(thisOperand), getToInt32(indexOperand)); @@ -1715,9 +1701,6 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins return false; int thisOperand = registerOffset + argumentToOperand(0); - if (!(m_graph[get(thisOperand)].prediction() & SpecString)) - return false; - int indexOperand = registerOffset + argumentToOperand(1); NodeIndex charCode = addToGraph(StringCharAt, OpInfo(Array::String), get(thisOperand), getToInt32(indexOperand)); @@ -1793,6 +1776,10 @@ NodeIndex ByteCodeParser::handleGetByOffset(SpeculatedType prediction, NodeIndex propertyStorage = base; else propertyStorage = addToGraph(GetButterfly, base); + // FIXME: It would be far more efficient for load elimination (and safer from + // an OSR standpoint) if GetByOffset also referenced the object we were loading + // from, and if we could load eliminate a GetByOffset even if the butterfly + // had changed. That would be a great success. NodeIndex getByOffset = addToGraph(GetByOffset, OpInfo(m_graph.m_storageAccessData.size()), OpInfo(prediction), propertyStorage); StorageAccessData storageAccessData; @@ -1888,12 +1875,12 @@ bool ByteCodeParser::parseResolveOperations(SpeculatedType prediction, unsigned while (resolvingBase) { switch (pc->m_operation) { case ResolveOperation::ReturnGlobalObjectAsBase: - *base = get(m_codeBlock->globalObjectConstant()); + *base = cellConstant(globalObject); ASSERT(!value); return true; case ResolveOperation::SetBaseToGlobal: - *base = get(m_codeBlock->globalObjectConstant()); + *base = cellConstant(globalObject); setBase = true; resolvingBase = false; ++pc; @@ -2150,7 +2137,11 @@ bool ByteCodeParser::parseBlock(unsigned limit) case op_new_array_buffer: { int startConstant = currentInstruction[2].u.operand; int numConstants = currentInstruction[3].u.operand; - set(currentInstruction[1].u.operand, addToGraph(NewArrayBuffer, OpInfo(m_inlineStackTop->m_constantBufferRemap[startConstant]), OpInfo(numConstants))); + NewArrayBufferData data; + data.startConstant = m_inlineStackTop->m_constantBufferRemap[startConstant]; + data.numConstants = numConstants; + m_graph.m_newArrayBufferData.append(data); + set(currentInstruction[1].u.operand, addToGraph(NewArrayBuffer, OpInfo(&m_graph.m_newArrayBufferData.last()))); NEXT_OPCODE(op_new_array_buffer); } @@ -2472,9 +2463,9 @@ bool ByteCodeParser::parseBlock(unsigned limit) SpeculatedType prediction = getPrediction(); NodeIndex base = get(currentInstruction[2].u.operand); - Array::Mode arrayMode = getArrayModeAndEmitChecks(currentInstruction[4].u.arrayProfile, Array::Read, base); + ArrayMode arrayMode = getArrayModeAndEmitChecks(currentInstruction[4].u.arrayProfile, Array::Read, base); NodeIndex property = get(currentInstruction[3].u.operand); - NodeIndex getByVal = addToGraph(GetByVal, OpInfo(arrayMode), OpInfo(prediction), base, property); + NodeIndex getByVal = addToGraph(GetByVal, OpInfo(arrayMode.asWord()), OpInfo(prediction), base, property); set(currentInstruction[1].u.operand, getByVal); NEXT_OPCODE(op_get_by_val); @@ -2483,7 +2474,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) case op_put_by_val: { NodeIndex base = get(currentInstruction[1].u.operand); - Array::Mode arrayMode = getArrayModeAndEmitChecks(currentInstruction[4].u.arrayProfile, Array::Write, base); + ArrayMode arrayMode = getArrayModeAndEmitChecks(currentInstruction[4].u.arrayProfile, Array::Write, base); NodeIndex property = get(currentInstruction[2].u.operand); NodeIndex value = get(currentInstruction[3].u.operand); @@ -2492,53 +2483,11 @@ bool ByteCodeParser::parseBlock(unsigned limit) addVarArgChild(property); addVarArgChild(value); addVarArgChild(NoNode); // Leave room for property storage. - addToGraph(Node::VarArg, PutByVal, OpInfo(arrayMode), OpInfo(0)); + addToGraph(Node::VarArg, PutByVal, OpInfo(arrayMode.asWord()), OpInfo(0)); NEXT_OPCODE(op_put_by_val); } - case op_method_check: { - m_currentProfilingIndex += OPCODE_LENGTH(op_method_check); - Instruction* getInstruction = currentInstruction + OPCODE_LENGTH(op_method_check); - - SpeculatedType prediction = getPrediction(); - - ASSERT(interpreter->getOpcodeID(getInstruction->u.opcode) == op_get_by_id - || interpreter->getOpcodeID(getInstruction->u.opcode) == op_get_by_id_out_of_line); - - NodeIndex base = get(getInstruction[2].u.operand); - unsigned identifier = m_inlineStackTop->m_identifierRemap[getInstruction[3].u.operand]; - - // Check if the method_check was monomorphic. If so, emit a CheckXYZMethod - // node, which is a lot more efficient. - GetByIdStatus getByIdStatus = GetByIdStatus::computeFor( - m_inlineStackTop->m_profiledBlock, - m_currentIndex, - m_codeBlock->identifier(identifier)); - MethodCallLinkStatus methodCallStatus = MethodCallLinkStatus::computeFor( - m_inlineStackTop->m_profiledBlock, m_currentIndex); - - if (methodCallStatus.isSet() - && !getByIdStatus.wasSeenInJIT() - && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCache)) { - // It's monomorphic as far as we can tell, since the method_check was linked - // but the slow path (i.e. the normal get_by_id) never fired. - - addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(methodCallStatus.structure())), base); - if (methodCallStatus.needsPrototypeCheck()) { - addStructureTransitionCheck( - methodCallStatus.prototype(), methodCallStatus.prototypeStructure()); - addToGraph(Phantom, base); - } - set(getInstruction[1].u.operand, cellConstant(methodCallStatus.function())); - } else { - handleGetById( - getInstruction[1].u.operand, prediction, base, identifier, getByIdStatus); - } - - m_currentIndex += OPCODE_LENGTH(op_method_check) + OPCODE_LENGTH(op_get_by_id); - continue; - } case op_get_by_id: case op_get_by_id_out_of_line: case op_get_array_length: { @@ -2665,6 +2614,10 @@ bool ByteCodeParser::parseBlock(unsigned limit) NEXT_OPCODE(op_put_by_id); } + case op_init_global_const_nop: { + NEXT_OPCODE(op_init_global_const_nop); + } + case op_init_global_const: { NodeIndex value = get(currentInstruction[2].u.operand); addToGraph( @@ -2902,10 +2855,10 @@ bool ByteCodeParser::parseBlock(unsigned limit) addToGraph(Throw, get(currentInstruction[1].u.operand)); LAST_OPCODE(op_throw); - case op_throw_reference_error: + case op_throw_static_error: flushArgumentsAndCapturedVariables(); addToGraph(ThrowReferenceError); - LAST_OPCODE(op_throw_reference_error); + LAST_OPCODE(op_throw_static_error); case op_call: handleCall(interpreter, currentInstruction, Call, CodeForCall); @@ -3104,6 +3057,14 @@ bool ByteCodeParser::parseBlock(unsigned limit) NodeIndex base = 0; NodeIndex value = 0; if (parseResolveOperations(prediction, identifier, operations, putToBaseOperation, &base, &value)) { + // First create OSR hints only. + set(baseDst, base); + set(valueDst, value); + + // If we try to hoist structure checks into here, then we're guaranteed that they will occur + // *after* we have already set up the values for OSR. + + // Then do the real SetLocals. set(baseDst, base); set(valueDst, value); } else { @@ -3124,6 +3085,14 @@ bool ByteCodeParser::parseBlock(unsigned limit) NodeIndex base = 0; NodeIndex value = 0; if (parseResolveOperations(prediction, identifier, operations, 0, &base, &value)) { + // First create OSR hints only. + set(baseDst, base); + set(valueDst, value); + + // If we try to hoist structure checks into here, then we're guaranteed that they will occur + // *after* we have already set up the values for OSR. + + // Then do the real SetLocals. set(baseDst, base); set(valueDst, value); } else { @@ -3632,7 +3601,7 @@ void ByteCodeParser::parseCodeBlock() dataLog("Parsing code block %p. codeType = %s, captureCount = %u, needsFullScopeChain = %s, needsActivation = %s, isStrictMode = %s\n", codeBlock, codeTypeToString(codeBlock->codeType()), - codeBlock->symbolTable()->captureCount(), + codeBlock->symbolTable() ? codeBlock->symbolTable()->captureCount() : 0, codeBlock->needsFullScopeChain()?"true":"false", codeBlock->ownerExecutable()->needsActivation()?"true":"false", codeBlock->ownerExecutable()->isStrictMode()?"true":"false"); diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp index 185332921..19051c174 100644 --- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp @@ -176,13 +176,8 @@ private: case PutByVal: if (!m_graph.byValIsPure(node)) return NoNode; - switch (node.arrayMode()) { - case CONTIGUOUS_TO_TAIL_MODES: - case ARRAY_STORAGE_TO_HOLE_MODES: + if (node.arrayMode().mayStoreToHole()) return NoNode; - default: - break; - } break; default: @@ -438,6 +433,12 @@ private: } return false; + case Arrayify: + case ArrayifyToStructure: + // We could check if the arrayification could affect our structures. + // But that seems like it would take Effort. + return false; + default: if (m_graph.clobbersWorld(index)) return false; @@ -489,6 +490,12 @@ private: return true; break; + case Arrayify: + case ArrayifyToStructure: + // We could check if the arrayification could affect our structures. + // But that seems like it would take Effort. + return false; + default: if (m_graph.clobbersWorld(index)) return false; @@ -663,7 +670,6 @@ private: case AllocatePropertyStorage: case ReallocatePropertyStorage: - case Arrayify: // If we can cheaply prove this is a change to our object's storage, we // can optimize and use its result. if (node.child1() == child1) @@ -689,6 +695,12 @@ private: } return NoNode; + case Arrayify: + case ArrayifyToStructure: + // We could check if the arrayification could affect our butterfly. + // But that seems like it would take Effort. + return NoNode; + default: if (m_graph.clobbersWorld(index)) return NoNode; @@ -698,7 +710,7 @@ private: return NoNode; } - bool checkArrayElimination(NodeIndex child1, Array::Mode arrayMode) + bool checkArrayElimination(NodeIndex child1, ArrayMode arrayMode) { for (unsigned i = m_indexInBlock; i--;) { NodeIndex index = m_currentBlock->at(i); @@ -720,6 +732,12 @@ private: return true; break; + case Arrayify: + case ArrayifyToStructure: + // We could check if the arrayification could affect our array. + // But that seems like it would take Effort. + return false; + default: if (m_graph.clobbersWorld(index)) return false; @@ -729,7 +747,7 @@ private: return false; } - NodeIndex getIndexedPropertyStorageLoadElimination(NodeIndex child1, Array::Mode arrayMode) + NodeIndex getIndexedPropertyStorageLoadElimination(NodeIndex child1, ArrayMode arrayMode) { for (unsigned i = m_indexInBlock; i--;) { NodeIndex index = m_currentBlock->at(i); @@ -788,8 +806,6 @@ private: } return NoNode; } - - NodeIndex getLocalLoadElimination(VirtualRegister local, NodeIndex& relevantLocalOp, bool careAboutClobbering) { @@ -1234,7 +1250,7 @@ private: case PutByVal: { Edge child1 = m_graph.varArgChild(node, 0); Edge child2 = m_graph.varArgChild(node, 1); - if (canCSEStorage(node.arrayMode())) { + if (node.arrayMode().canCSEStorage()) { NodeIndex nodeIndex = getByValLoadElimination(child1.index(), child2.index()); if (nodeIndex == NoNode) break; diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.h b/Source/JavaScriptCore/dfg/DFGCapabilities.h index e80cc28ae..1f9778efe 100644 --- a/Source/JavaScriptCore/dfg/DFGCapabilities.h +++ b/Source/JavaScriptCore/dfg/DFGCapabilities.h @@ -157,7 +157,6 @@ inline CapabilityLevel canCompileOpcode(OpcodeID opcodeID, CodeBlock*, Instructi case op_nstricteq: case op_get_by_val: case op_put_by_val: - case op_method_check: case op_get_by_id: case op_get_by_id_out_of_line: case op_get_array_length: @@ -167,6 +166,7 @@ inline CapabilityLevel canCompileOpcode(OpcodeID opcodeID, CodeBlock*, Instructi case op_put_by_id_transition_direct_out_of_line: case op_put_by_id_transition_normal: case op_put_by_id_transition_normal_out_of_line: + case op_init_global_const_nop: case op_init_global_const: case op_init_global_const_check: case op_jmp: @@ -200,7 +200,7 @@ inline CapabilityLevel canCompileOpcode(OpcodeID opcodeID, CodeBlock*, Instructi case op_strcat: case op_to_primitive: case op_throw: - case op_throw_reference_error: + case op_throw_static_error: case op_call: case op_construct: case op_new_regexp: diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp index 25915cfd4..43aa2c007 100644 --- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp @@ -92,10 +92,22 @@ private: } case CheckStructure: - case ForwardCheckStructure: { + case ForwardCheckStructure: + case ArrayifyToStructure: { AbstractValue& value = m_state.forNode(node.child1()); + StructureSet set; + if (node.op() == ArrayifyToStructure) + set = node.structure(); + else + set = node.structureSet(); + if (value.m_currentKnownStructure.isSubsetOf(set)) { + ASSERT(node.refCount() == 1); + node.setOpAndDefaultFlags(Phantom); + eliminated = true; + break; + } StructureAbstractValue& structureValue = value.m_futurePossibleStructure; - if (structureValue.isSubsetOf(node.structureSet()) + if (structureValue.isSubsetOf(set) && structureValue.hasSingleton() && isCellSpeculation(value.m_type)) node.convertToStructureTransitionWatchpoint(structureValue.singleton()); @@ -104,7 +116,7 @@ private: case CheckArray: case Arrayify: { - if (!modeAlreadyChecked(m_state.forNode(node.child1()), node.arrayMode())) + if (!node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))) break; ASSERT(node.refCount() == 1); node.setOpAndDefaultFlags(Phantom); diff --git a/Source/JavaScriptCore/dfg/DFGFPRInfo.h b/Source/JavaScriptCore/dfg/DFGFPRInfo.h index 5ee87bce1..d6a038a99 100644 --- a/Source/JavaScriptCore/dfg/DFGFPRInfo.h +++ b/Source/JavaScriptCore/dfg/DFGFPRInfo.h @@ -82,7 +82,7 @@ public: { ASSERT(reg != InvalidFPRReg); #if CPU(X86_64) - ASSERT(reg < 16); + ASSERT(static_cast<int>(reg) < 16); static const char* nameForRegister[16] = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", @@ -90,7 +90,7 @@ public: "xmm12", "xmm13", "xmm14", "xmm15" }; #elif CPU(X86) - ASSERT(reg < 8); + ASSERT(static_cast<int>(reg) < 8); static const char* nameForRegister[8] = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7" diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index 5dcfe08a5..5a76aa8df 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -86,14 +86,14 @@ private: ArrayProfile* arrayProfile = m_graph.baselineCodeBlockFor(nodePtr->codeOrigin)->getArrayProfile( nodePtr->codeOrigin.bytecodeIndex); - Array::Mode arrayMode = Array::SelectUsingPredictions; + ArrayMode arrayMode = ArrayMode(Array::SelectUsingPredictions); if (arrayProfile) { - arrayProfile->computeUpdatedPrediction(); - arrayMode = refineArrayMode( - fromObserved(arrayProfile, Array::Read, false), + arrayProfile->computeUpdatedPrediction(m_graph.baselineCodeBlockFor(node.codeOrigin)); + arrayMode = ArrayMode::fromObserved(arrayProfile, Array::Read, false); + arrayMode = arrayMode.refine( m_graph[node.child1()].prediction(), m_graph[m_compileIndex].prediction()); - if (modeSupportsLength(arrayMode) && arrayProfile->hasDefiniteStructure()) { + if (arrayMode.supportsLength() && arrayProfile->hasDefiniteStructure()) { m_graph.ref(nodePtr->child1()); Node checkStructure(CheckStructure, nodePtr->codeOrigin, OpInfo(m_graph.addStructureSet(arrayProfile->expectedStructure())), nodePtr->child1().index()); checkStructure.ref(); @@ -103,12 +103,11 @@ private: nodePtr = &m_graph[m_compileIndex]; } } else { - arrayMode = refineArrayMode( - arrayMode, + arrayMode = arrayMode.refine( m_graph[node.child1()].prediction(), m_graph[m_compileIndex].prediction()); } - if (!modeSupportsLength(arrayMode)) + if (!arrayMode.supportsLength()) break; nodePtr->setOp(GetArrayLength); ASSERT(nodePtr->flags() & NodeMustGenerate); @@ -125,21 +124,25 @@ private: break; } case GetIndexedPropertyStorage: { - ASSERT(canCSEStorage(node.arrayMode())); + ASSERT(node.arrayMode().canCSEStorage()); break; } - case GetByVal: - case StringCharAt: - case StringCharCodeAt: { + case GetByVal: { node.setArrayMode( - refineArrayMode( - node.arrayMode(), + node.arrayMode().refine( m_graph[node.child1()].prediction(), m_graph[node.child2()].prediction())); blessArrayOperation(node.child1(), node.child2(), 2); break; } + case StringCharAt: + case StringCharCodeAt: { + // Currently we have no good way of refining these. + ASSERT(node.arrayMode() == ArrayMode(Array::String)); + blessArrayOperation(node.child1(), node.child2(), 2); + break; + } case ArrayPush: { blessArrayOperation(node.child1(), node.child2(), 2); @@ -323,8 +326,7 @@ private: Edge child3 = m_graph.varArgChild(node, 2); node.setArrayMode( - refineArrayMode( - node.arrayMode(), + node.arrayMode().refine( m_graph[child1].prediction(), m_graph[child2].prediction())); @@ -332,7 +334,7 @@ private: Node* nodePtr = &m_graph[m_compileIndex]; - switch (modeForPut(nodePtr->arrayMode())) { + switch (nodePtr->arrayMode().modeForPut().type()) { case Array::Int8Array: case Array::Int16Array: case Array::Int32Array: @@ -376,47 +378,66 @@ private: return nodeIndex; } - NodeIndex checkArray(Array::Mode arrayMode, CodeOrigin codeOrigin, NodeIndex array, NodeIndex index, bool (*storageCheck)(Array::Mode) = canCSEStorage, bool shouldGenerate = true) + NodeIndex checkArray(ArrayMode arrayMode, CodeOrigin codeOrigin, NodeIndex array, NodeIndex index, bool (*storageCheck)(const ArrayMode&) = canCSEStorage, bool shouldGenerate = true) { - ASSERT(modeIsSpecific(arrayMode)); + ASSERT(arrayMode.isSpecific()); m_graph.ref(array); - if (isEffectful(arrayMode)) { + if (arrayMode.doesConversion()) { if (index != NoNode) m_graph.ref(index); - Node arrayify(Arrayify, codeOrigin, OpInfo(arrayMode), array, index); - arrayify.ref(); // Once because it's used as a butterfly. - arrayify.ref(); // And twice because it's must-generate. - NodeIndex arrayifyIndex = m_graph.size(); - m_graph.append(arrayify); - m_insertionSet.append(m_indexInBlock, arrayifyIndex); - - ASSERT(shouldGenerate); - ASSERT(canCSEStorage(arrayMode)); - ASSERT(modeUsesButterfly(arrayMode)); - - if (!storageCheck(arrayMode)) - return NoNode; - return arrayifyIndex; + + Structure* structure = 0; + if (arrayMode.isJSArrayWithOriginalStructure()) { + JSGlobalObject* globalObject = m_graph.baselineCodeBlockFor(codeOrigin)->globalObject(); + switch (arrayMode.type()) { + case Array::Contiguous: + structure = globalObject->arrayStructure(); + if (structure->indexingType() != ArrayWithContiguous) + structure = 0; + break; + case Array::ArrayStorage: + structure = globalObject->arrayStructureWithArrayStorage(); + if (structure->indexingType() != ArrayWithArrayStorage) + structure = 0; + break; + default: + break; + } + } + + if (structure) { + Node arrayify(ArrayifyToStructure, codeOrigin, OpInfo(structure), OpInfo(arrayMode.asWord()), array, index); + arrayify.ref(); + NodeIndex arrayifyIndex = m_graph.size(); + m_graph.append(arrayify); + m_insertionSet.append(m_indexInBlock, arrayifyIndex); + } else { + Node arrayify(Arrayify, codeOrigin, OpInfo(arrayMode.asWord()), array, index); + arrayify.ref(); + NodeIndex arrayifyIndex = m_graph.size(); + m_graph.append(arrayify); + m_insertionSet.append(m_indexInBlock, arrayifyIndex); + } + } else { + Node checkArray(CheckArray, codeOrigin, OpInfo(arrayMode.asWord()), array); + checkArray.ref(); + NodeIndex checkArrayIndex = m_graph.size(); + m_graph.append(checkArray); + m_insertionSet.append(m_indexInBlock, checkArrayIndex); } - Node checkArray(CheckArray, codeOrigin, OpInfo(arrayMode), array); - checkArray.ref(); - NodeIndex checkArrayIndex = m_graph.size(); - m_graph.append(checkArray); - m_insertionSet.append(m_indexInBlock, checkArrayIndex); - if (!storageCheck(arrayMode)) return NoNode; if (shouldGenerate) m_graph.ref(array); - if (modeUsesButterfly(arrayMode)) + if (arrayMode.usesButterfly()) return addNode(Node(GetButterfly, codeOrigin, array), shouldGenerate); - return addNode(Node(GetIndexedPropertyStorage, codeOrigin, OpInfo(arrayMode), array), shouldGenerate); + return addNode(Node(GetIndexedPropertyStorage, codeOrigin, OpInfo(arrayMode.asWord()), array), shouldGenerate); } void blessArrayOperation(Edge base, Edge index, unsigned storageChildIdx) @@ -426,7 +447,7 @@ private: Node* nodePtr = &m_graph[m_compileIndex]; - switch (nodePtr->arrayMode()) { + switch (nodePtr->arrayMode().type()) { case Array::ForceExit: { Node forceExit(ForceOSRExit, nodePtr->codeOrigin); forceExit.ref(); diff --git a/Source/JavaScriptCore/dfg/DFGGPRInfo.h b/Source/JavaScriptCore/dfg/DFGGPRInfo.h index 498b116ec..3d07556cc 100644 --- a/Source/JavaScriptCore/dfg/DFGGPRInfo.h +++ b/Source/JavaScriptCore/dfg/DFGGPRInfo.h @@ -218,7 +218,7 @@ public: GPRReg tagGPR() const { - ASSERT(!isAddress() && m_baseOrTag != InvalidGPRReg); + ASSERT(!isAddress() && static_cast<GPRReg>(m_baseOrTag) != InvalidGPRReg); return static_cast<GPRReg>(m_baseOrTag); } @@ -290,7 +290,7 @@ public: static unsigned toIndex(GPRReg reg) { ASSERT(reg != InvalidGPRReg); - ASSERT(reg < 8); + ASSERT(static_cast<int>(reg) < 8); static const unsigned indexForRegister[8] = { 0, 2, 1, 3, InvalidIndex, InvalidIndex, 4, InvalidIndex }; unsigned result = indexForRegister[reg]; ASSERT(result != InvalidIndex); @@ -300,7 +300,7 @@ public: static const char* debugName(GPRReg reg) { ASSERT(reg != InvalidGPRReg); - ASSERT(reg < 8); + ASSERT(static_cast<int>(reg) < 8); static const char* nameForRegister[8] = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", @@ -362,7 +362,7 @@ public: static unsigned toIndex(GPRReg reg) { ASSERT(reg != InvalidGPRReg); - ASSERT(reg < 16); + ASSERT(static_cast<int>(reg) < 16); static const unsigned indexForRegister[16] = { 0, 2, 1, 3, InvalidIndex, InvalidIndex, 5, 4, 6, 7, 8, InvalidIndex, InvalidIndex, InvalidIndex, InvalidIndex, InvalidIndex }; unsigned result = indexForRegister[reg]; ASSERT(result != InvalidIndex); @@ -372,7 +372,7 @@ public: static const char* debugName(GPRReg reg) { ASSERT(reg != InvalidGPRReg); - ASSERT(reg < 16); + ASSERT(static_cast<int>(reg) < 16); static const char* nameForRegister[16] = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp index b9a0db2a2..8e8817f81 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.cpp +++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp @@ -226,7 +226,7 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) hasPrinted = true; } if (node.hasArrayMode()) { - dataLog("%s%s", hasPrinted ? ", " : "", modeToString(node.arrayMode())); + dataLog("%s%s", hasPrinted ? ", " : "", node.arrayMode().toString()); hasPrinted = true; } if (node.hasVarNumber()) { diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h index b2c754f85..9fbb2df07 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.h +++ b/Source/JavaScriptCore/dfg/DFGGraph.h @@ -490,14 +490,14 @@ public: // - and so on. bool byValIsPure(Node& node) { - switch (node.arrayMode()) { + switch (node.arrayMode().type()) { case Array::Generic: - case OUT_OF_BOUNDS_CONTIGUOUS_MODES: - case ARRAY_STORAGE_TO_HOLE_MODES: - case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: - case SLOW_PUT_ARRAY_STORAGE_MODES: - case ALL_EFFECTFUL_MODES: return false; + case Array::Contiguous: + case Array::ArrayStorage: + return !node.arrayMode().isOutOfBounds(); + case Array::SlowPutArrayStorage: + return !node.arrayMode().mayStoreToHole(); case Array::String: return node.op() == GetByVal; #if USE(JSVALUE32_64) @@ -689,6 +689,7 @@ public: SegmentedVector<ArgumentPosition, 8> m_argumentPositions; SegmentedVector<StructureSet, 16> m_structureSet; SegmentedVector<StructureTransitionData, 8> m_structureTransitionData; + SegmentedVector<NewArrayBufferData, 4> m_newArrayBufferData; bool m_hasArguments; HashSet<ExecutableBase*> m_executablesWhoseArgumentsEscaped; BitVector m_preservedVars; diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h index 40b3ed7ec..e66629ec4 100644 --- a/Source/JavaScriptCore/dfg/DFGNode.h +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -59,6 +59,11 @@ struct StructureTransitionData { } }; +struct NewArrayBufferData { + unsigned startConstant; + unsigned numConstants; +}; + // This type used in passing an immediate argument to Node constructor; // distinguishes an immediate value (typically an index into a CodeBlock data structure - // a constant index, argument, or identifier) from a NodeIndex. @@ -250,9 +255,9 @@ struct Node { void convertToStructureTransitionWatchpoint(Structure* structure) { - ASSERT(m_op == CheckStructure || m_op == ForwardCheckStructure); + ASSERT(m_op == CheckStructure || m_op == ForwardCheckStructure || m_op == ArrayifyToStructure); m_opInfo = bitwise_cast<uintptr_t>(structure); - if (m_op == CheckStructure) + if (m_op == CheckStructure || m_op == ArrayifyToStructure) m_op = StructureTransitionWatchpoint; else m_op = ForwardStructureTransitionWatchpoint; @@ -412,16 +417,20 @@ struct Node { return op() == NewArrayBuffer; } - unsigned startConstant() + NewArrayBufferData* newArrayBufferData() { ASSERT(hasConstantBuffer()); - return m_opInfo; + return reinterpret_cast<NewArrayBufferData*>(m_opInfo); + } + + unsigned startConstant() + { + return newArrayBufferData()->startConstant; } unsigned numConstants() { - ASSERT(hasConstantBuffer()); - return m_opInfo2; + return newArrayBufferData()->numConstants; } bool hasRegexpIndex() @@ -692,6 +701,7 @@ struct Node { switch (op()) { case StructureTransitionWatchpoint: case ForwardStructureTransitionWatchpoint: + case ArrayifyToStructure: return true; default: return false; @@ -750,6 +760,7 @@ struct Node { case StringCharCodeAt: case CheckArray: case Arrayify: + case ArrayifyToStructure: case ArrayPush: case ArrayPop: return true; @@ -758,18 +769,20 @@ struct Node { } } - Array::Mode arrayMode() + ArrayMode arrayMode() { ASSERT(hasArrayMode()); - return static_cast<Array::Mode>(m_opInfo); + if (op() == ArrayifyToStructure) + return ArrayMode::fromWord(m_opInfo2); + return ArrayMode::fromWord(m_opInfo); } - bool setArrayMode(Array::Mode arrayMode) + bool setArrayMode(ArrayMode arrayMode) { ASSERT(hasArrayMode()); if (this->arrayMode() == arrayMode) return false; - m_opInfo = arrayMode; + m_opInfo = arrayMode.asWord(); return true; } diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h index 1d2460659..624b1ae75 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeType.h +++ b/Source/JavaScriptCore/dfg/DFGNodeType.h @@ -139,7 +139,8 @@ namespace JSC { namespace DFG { macro(ReallocatePropertyStorage, NodeMustGenerate | NodeDoesNotExit | NodeResultStorage) \ macro(GetButterfly, NodeResultStorage) \ macro(CheckArray, NodeMustGenerate) \ - macro(Arrayify, NodeResultStorage | NodeMustGenerate | NodeClobbersWorld) \ + macro(Arrayify, NodeMustGenerate) \ + macro(ArrayifyToStructure, NodeMustGenerate) \ macro(GetIndexedPropertyStorage, NodeResultStorage) \ macro(GetByOffset, NodeResultJS) \ macro(PutByOffset, NodeMustGenerate) \ diff --git a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp index 6560088fd..b3701722e 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp @@ -45,9 +45,9 @@ OSRExit::OSRExit(ExitKind kind, JSValueSource jsValueSource, MethodOfGettingAVal , m_kind(kind) , m_count(0) , m_streamIndex(streamIndex) + , m_lastSetOperand(jit->m_lastSetOperand) { ASSERT(m_codeOrigin.isSet()); - m_setOperands.append(jit->m_lastSetOperand); } bool OSRExit::considerAddingAsFrequentExitSiteSlow(CodeBlock* dfgCodeBlock, CodeBlock* profiledCodeBlock) diff --git a/Source/JavaScriptCore/dfg/DFGOSRExit.h b/Source/JavaScriptCore/dfg/DFGOSRExit.h index 0ecefe386..cd2434c11 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExit.h +++ b/Source/JavaScriptCore/dfg/DFGOSRExit.h @@ -110,9 +110,9 @@ struct OSRExit { } unsigned m_streamIndex; - Vector<int, 1> m_setOperands; + int m_lastSetOperand; - Vector<RefPtr<ValueRecoveryOverride>, 1> m_valueRecoveryOverrides; + RefPtr<ValueRecoveryOverride> m_valueRecoveryOverride; private: bool considerAddingAsFrequentExitSiteSlow(CodeBlock* dfgCodeBlock, CodeBlock* profiledCodeBlock); diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp index 55a903c7a..2ce1c887b 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp @@ -70,10 +70,11 @@ void compileOSRExit(ExecState* exec) Operands<ValueRecovery> operands; codeBlock->variableEventStream().reconstruct(codeBlock, exit.m_codeOrigin, codeBlock->minifiedDFG(), exit.m_streamIndex, operands); - // There may be overrides, for forward speculations. - for (size_t i = 0; i < exit.m_valueRecoveryOverrides.size(); i++) - operands.setOperand(exit.m_valueRecoveryOverrides[i]->operand, exit.m_valueRecoveryOverrides[i]->recovery); - + // There may be an override, for forward speculations. + if (!!exit.m_valueRecoveryOverride) { + operands.setOperand( + exit.m_valueRecoveryOverride->operand, exit.m_valueRecoveryOverride->recovery); + } SpeculationRecovery* recovery = 0; if (exit.m_recoveryIndex) diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp index b64ce3fa1..df4f3c905 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp @@ -688,6 +688,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov } } + if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments()) + continue; int argumentsRegister = m_jit.argumentsRegisterFor(inlineCallFrame); if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) { // We know this call frame optimized out an arguments object that @@ -732,9 +734,9 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov // 15) Load the result of the last bytecode operation into regT0. - for (size_t i = 0; i < exit.m_setOperands.size(); i++) { - m_jit.load32(AssemblyHelpers::payloadFor((VirtualRegister)exit.m_setOperands[i]), GPRInfo::cachedResultRegister); - m_jit.load32(AssemblyHelpers::tagFor((VirtualRegister)exit.m_setOperands[i]), GPRInfo::cachedResultRegister2); + if (exit.m_lastSetOperand != std::numeric_limits<int>::max()) { + m_jit.load32(AssemblyHelpers::payloadFor((VirtualRegister)exit.m_lastSetOperand), GPRInfo::cachedResultRegister); + m_jit.load32(AssemblyHelpers::tagFor((VirtualRegister)exit.m_lastSetOperand), GPRInfo::cachedResultRegister2); } // 16) Adjust the call frame pointer. diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp index 65b89a550..b278997ab 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp @@ -653,6 +653,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov } } + if (!m_jit.baselineCodeBlockFor(inlineCallFrame)->usesArguments()) + continue; int argumentsRegister = m_jit.argumentsRegisterFor(inlineCallFrame); if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) { // We know this call frame optimized out an arguments object that @@ -681,9 +683,9 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov // 16) Load the result of the last bytecode operation into regT0. - for (size_t i = 0; i < exit.m_setOperands.size(); i++) - m_jit.load64(AssemblyHelpers::addressFor((VirtualRegister)exit.m_setOperands[i]), GPRInfo::cachedResultRegister); - + if (exit.m_lastSetOperand != std::numeric_limits<int>::max()) + m_jit.loadPtr(AssemblyHelpers::addressFor((VirtualRegister)exit.m_lastSetOperand), GPRInfo::cachedResultRegister); + // 17) Adjust the call frame pointer. if (exit.m_codeOrigin.inlineCallFrame) diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index 13e04388c..0e45e230c 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -1157,8 +1157,7 @@ JSCell* DFG_OPERATION operationCreateActivation(ExecState* exec) { JSGlobalData& globalData = exec->globalData(); NativeCallFrameTracer tracer(&globalData, exec); - JSActivation* activation = JSActivation::create( - globalData, exec, static_cast<FunctionExecutable*>(exec->codeBlock()->ownerExecutable())); + JSActivation* activation = JSActivation::create(globalData, exec, exec->codeBlock()); exec->setScope(activation); return activation; } diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index fee7a3ca2..3e8ead5c6 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -635,7 +635,8 @@ private: case PhantomPutStructure: case PhantomArguments: case CheckArray: - case Arrayify: { + case Arrayify: + case ArrayifyToStructure: { // This node should never be visible at this stage of compilation. It is // inserted by fixup(), which follows this phase. ASSERT_NOT_REACHED(); diff --git a/Source/JavaScriptCore/dfg/DFGRepatch.cpp b/Source/JavaScriptCore/dfg/DFGRepatch.cpp index 531a525d5..7c15ef33e 100644 --- a/Source/JavaScriptCore/dfg/DFGRepatch.cpp +++ b/Source/JavaScriptCore/dfg/DFGRepatch.cpp @@ -324,7 +324,7 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier PropertyOffset offset = slot.cachedOffset(); size_t count = normalizePrototypeChain(exec, baseValue, slot.slotBase(), propertyName, offset); - if (!count) + if (count == InvalidPrototypeChain) return false; StructureChain* prototypeChain = structure->prototypeChain(exec); @@ -550,7 +550,7 @@ static bool tryBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const I PropertyOffset offset = slot.cachedOffset(); size_t count = normalizePrototypeChain(exec, baseValue, slot.slotBase(), propertyName, offset); - if (!count) + if (count == InvalidPrototypeChain) return false; Structure* structure = baseValue.asCell()->structure(); @@ -964,7 +964,8 @@ static bool tryCachePutByID(ExecState* exec, JSValue baseValue, const Identifier if (hasIndexingHeader(oldStructure->indexingType())) return false; - normalizePrototypeChain(exec, baseCell); + if (normalizePrototypeChain(exec, baseCell) == InvalidPrototypeChain) + return false; StructureChain* prototypeChain = structure->prototypeChain(exec); @@ -1035,7 +1036,8 @@ static bool tryBuildPutByIdList(ExecState* exec, JSValue baseValue, const Identi if (hasIndexingHeader(oldStructure->indexingType())) return false; - normalizePrototypeChain(exec, baseCell); + if (normalizePrototypeChain(exec, baseCell) == InvalidPrototypeChain) + return false; StructureChain* prototypeChain = structure->prototypeChain(exec); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 7cb028388..6bedd6d68 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -153,19 +153,37 @@ JumpReplacementWatchpoint* SpeculativeJIT::speculationWatchpoint(ExitKind kind) void SpeculativeJIT::convertLastOSRExitToForward(const ValueRecovery& valueRecovery) { -#if !ASSERT_DISABLED if (!valueRecovery) { // Check that the preceding node was a SetLocal with the same code origin. Node* setLocal = &at(m_jit.graph().m_blocks[m_block]->at(m_indexInBlock - 1)); - ASSERT(setLocal->op() == SetLocal); - ASSERT(setLocal->codeOrigin == at(m_compileIndex).codeOrigin); + ASSERT_UNUSED(setLocal, setLocal->op() == SetLocal); + ASSERT_UNUSED(setLocal, setLocal->codeOrigin == at(m_compileIndex).codeOrigin); + + // Find the next node. + unsigned indexInBlock = m_indexInBlock + 1; + Node* node = 0; + for (;;) { + if (indexInBlock == m_jit.graph().m_blocks[m_block]->size()) { + // This is an inline return. Give up and do a backwards speculation. This is safe + // because an inline return has its own bytecode index and it's always safe to + // reexecute that bytecode. + ASSERT(node->op() == Jump); + return; + } + node = &at(m_jit.graph().m_blocks[m_block]->at(indexInBlock)); + if (node->codeOrigin != at(m_compileIndex).codeOrigin) + break; + indexInBlock++; + } + + ASSERT(node->codeOrigin != at(m_compileIndex).codeOrigin); + OSRExit& exit = m_jit.codeBlock()->lastOSRExit(); + exit.m_codeOrigin = node->codeOrigin; + return; } -#endif unsigned setLocalIndexInBlock = m_indexInBlock + 1; - - OSRExit& exit = m_jit.codeBlock()->lastOSRExit(); - + Node* setLocal = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock)); bool hadInt32ToDouble = false; @@ -175,13 +193,11 @@ void SpeculativeJIT::convertLastOSRExitToForward(const ValueRecovery& valueRecov } if (setLocal->op() == Flush || setLocal->op() == Phantom) setLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock)); - - if (!!valueRecovery) { - if (hadInt32ToDouble) - ASSERT(at(setLocal->child1()).child1() == m_compileIndex); - else - ASSERT(setLocal->child1() == m_compileIndex); - } + + if (hadInt32ToDouble) + ASSERT(at(setLocal->child1()).child1() == m_compileIndex); + else + ASSERT(setLocal->child1() == m_compileIndex); ASSERT(setLocal->op() == SetLocal); ASSERT(setLocal->codeOrigin == at(m_compileIndex).codeOrigin); @@ -190,34 +206,14 @@ void SpeculativeJIT::convertLastOSRExitToForward(const ValueRecovery& valueRecov // We're at an inlined return. Use a backward speculation instead. return; } - - exit.m_setOperands[0] = setLocal->local(); - while (nextNode->codeOrigin == at(m_compileIndex).codeOrigin) { - ++setLocalIndexInBlock; - Node* nextSetLocal = nextNode; - if (nextSetLocal->op() == Int32ToDouble) - nextSetLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock)); - - if (nextSetLocal->op() == Flush || nextSetLocal->op() == Phantom) - nextSetLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock)); - - nextNode = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock + 1)); - ASSERT(nextNode->op() != Jump || nextNode->codeOrigin != at(m_compileIndex).codeOrigin); - ASSERT(nextSetLocal->op() == SetLocal); - exit.m_setOperands.append(nextSetLocal->local()); - } - ASSERT(nextNode->codeOrigin != at(m_compileIndex).codeOrigin); - + + OSRExit& exit = m_jit.codeBlock()->lastOSRExit(); exit.m_codeOrigin = nextNode->codeOrigin; - if (!valueRecovery) - return; - - ASSERT(exit.m_setOperands.size() == 1); - for (size_t i = 0; i < exit.m_setOperands.size(); i++) - exit.m_valueRecoveryOverrides.append(adoptRef(new ValueRecoveryOverride(exit.m_setOperands[i], valueRecovery))); - + exit.m_lastSetOperand = setLocal->local(); + exit.m_valueRecoveryOverride = adoptRef( + new ValueRecoveryOverride(setLocal->local(), valueRecovery)); } JumpReplacementWatchpoint* SpeculativeJIT::forwardSpeculationWatchpoint(ExitKind kind) @@ -321,9 +317,9 @@ void SpeculativeJIT::clearGenerationInfo() m_fprs = RegisterBank<FPRInfo>(); } -const TypedArrayDescriptor* SpeculativeJIT::typedArrayDescriptor(Array::Mode arrayMode) +const TypedArrayDescriptor* SpeculativeJIT::typedArrayDescriptor(ArrayMode arrayMode) { - switch (arrayMode) { + switch (arrayMode.type()) { case Array::Int8Array: return &m_jit.globalData()->int8ArrayDescriptor(); case Array::Int16Array: @@ -341,62 +337,75 @@ const TypedArrayDescriptor* SpeculativeJIT::typedArrayDescriptor(Array::Mode arr case Array::Float32Array: return &m_jit.globalData()->float32ArrayDescriptor(); case Array::Float64Array: - return &m_jit.globalData()->float32ArrayDescriptor(); + return &m_jit.globalData()->float64ArrayDescriptor(); default: return 0; } } -JITCompiler::JumpList SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, Array::Mode arrayMode) +JITCompiler::JumpList SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, ArrayMode arrayMode, bool invert) { JITCompiler::JumpList result; - switch (arrayMode) { - case NON_ARRAY_CONTIGUOUS_MODES: { + switch (arrayMode.type()) { + case Array::Contiguous: { + if (arrayMode.isJSArray()) { + m_jit.and32(TrustedImm32(IsArray | IndexingShapeMask), tempGPR); + result.append( + m_jit.branch32( + invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(IsArray | ContiguousShape))); + break; + } m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR); result.append( - m_jit.branch32(MacroAssembler::NotEqual, tempGPR, TrustedImm32(ContiguousShape))); + m_jit.branch32(invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(ContiguousShape))); break; } - case ARRAY_WITH_CONTIGUOUS_MODES: { - m_jit.and32(TrustedImm32(IsArray | IndexingShapeMask), tempGPR); - result.append( - m_jit.branch32( - MacroAssembler::NotEqual, tempGPR, TrustedImm32(IsArray | ContiguousShape))); - break; - } - case NON_ARRAY_ARRAY_STORAGE_MODES: { + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: { + if (arrayMode.isJSArray()) { + if (arrayMode.isSlowPut()) { + if (invert) { + JITCompiler::Jump slow = + m_jit.branchTest32( + MacroAssembler::Zero, tempGPR, MacroAssembler::TrustedImm32(IsArray)); + m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR); + m_jit.sub32(TrustedImm32(ArrayStorageShape), tempGPR); + result.append( + m_jit.branch32( + MacroAssembler::BelowOrEqual, tempGPR, + TrustedImm32(SlowPutArrayStorageShape - ArrayStorageShape))); + + slow.link(&m_jit); + } + + result.append( + m_jit.branchTest32( + MacroAssembler::Zero, tempGPR, MacroAssembler::TrustedImm32(IsArray))); + m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR); + m_jit.sub32(TrustedImm32(ArrayStorageShape), tempGPR); + result.append( + m_jit.branch32( + MacroAssembler::Above, tempGPR, + TrustedImm32(SlowPutArrayStorageShape - ArrayStorageShape))); + break; + } + m_jit.and32(TrustedImm32(IsArray | IndexingShapeMask), tempGPR); + result.append( + m_jit.branch32(invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(ArrayStorageShape))); + break; + } m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR); - if (isSlowPutAccess(arrayMode)) { + if (arrayMode.isSlowPut()) { m_jit.sub32(TrustedImm32(ArrayStorageShape), tempGPR); result.append( m_jit.branch32( - MacroAssembler::Above, tempGPR, + invert ? MacroAssembler::BelowOrEqual : MacroAssembler::Above, tempGPR, TrustedImm32(SlowPutArrayStorageShape - ArrayStorageShape))); - } else { - result.append( - m_jit.branch32(MacroAssembler::NotEqual, tempGPR, TrustedImm32(ArrayStorageShape))); + break; } - break; - } - case Array::ArrayWithArrayStorage: - case Array::ArrayWithArrayStorageToHole: - case Array::ArrayWithArrayStorageOutOfBounds: { - m_jit.and32(TrustedImm32(IsArray | IndexingShapeMask), tempGPR); result.append( - m_jit.branch32(MacroAssembler::NotEqual, tempGPR, TrustedImm32(ArrayStorageShape))); - break; - } - case Array::ArrayWithSlowPutArrayStorage: { - result.append( - m_jit.branchTest32( - MacroAssembler::Zero, tempGPR, MacroAssembler::TrustedImm32(IsArray))); - m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR); - m_jit.sub32(TrustedImm32(ArrayStorageShape), tempGPR); - result.append( - m_jit.branch32( - MacroAssembler::Above, tempGPR, - TrustedImm32(SlowPutArrayStorageShape - ArrayStorageShape))); + m_jit.branch32(invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(ArrayStorageShape))); break; } default: @@ -409,28 +418,28 @@ JITCompiler::JumpList SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGP void SpeculativeJIT::checkArray(Node& node) { - ASSERT(modeIsSpecific(node.arrayMode())); + ASSERT(node.arrayMode().isSpecific()); + ASSERT(!node.arrayMode().doesConversion()); SpeculateCellOperand base(this, node.child1()); GPRReg baseReg = base.gpr(); const TypedArrayDescriptor* result = typedArrayDescriptor(node.arrayMode()); - if (modeAlreadyChecked(m_state.forNode(node.child1()), node.arrayMode())) { + if (node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))) { noResult(m_compileIndex); return; } const ClassInfo* expectedClassInfo = 0; - switch (node.arrayMode()) { + switch (node.arrayMode().type()) { case Array::String: expectedClassInfo = &JSString::s_info; break; - case NON_ARRAY_CONTIGUOUS_MODES: - case ARRAY_WITH_CONTIGUOUS_MODES: - case NON_ARRAY_ARRAY_STORAGE_MODES: - case ARRAY_WITH_ARRAY_STORAGE_MODES: { + case Array::Contiguous: + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: { GPRTemporary temp(this); GPRReg tempGPR = temp.gpr(); m_jit.loadPtr( @@ -477,77 +486,63 @@ void SpeculativeJIT::checkArray(Node& node) void SpeculativeJIT::arrayify(Node& node, GPRReg baseReg, GPRReg propertyReg) { - Array::Mode desiredArrayMode; - - switch (node.arrayMode()) { - case Array::ToContiguous: - desiredArrayMode = Array::Contiguous; - break; - case Array::ToArrayStorage: - desiredArrayMode = Array::ArrayStorage; - break; - case Array::ToSlowPutArrayStorage: - desiredArrayMode = Array::SlowPutArrayStorage; - break; - case Array::ArrayToArrayStorage: - desiredArrayMode = Array::ArrayWithArrayStorage; - break; - case Array::PossiblyArrayToArrayStorage: - desiredArrayMode = Array::PossiblyArrayWithArrayStorage; - break; - default: - CRASH(); - desiredArrayMode = Array::ForceExit; - break; - } + ASSERT(node.arrayMode().doesConversion()); - GPRTemporary structure(this); GPRTemporary temp(this); - GPRReg structureGPR = structure.gpr(); + GPRTemporary structure; GPRReg tempGPR = temp.gpr(); - - m_jit.loadPtr( - MacroAssembler::Address(baseReg, JSCell::structureOffset()), structureGPR); + GPRReg structureGPR = InvalidGPRReg; - m_jit.load8( - MacroAssembler::Address(structureGPR, Structure::indexingTypeOffset()), tempGPR); + if (node.op() != ArrayifyToStructure) { + GPRTemporary realStructure(this); + structure.adopt(realStructure); + structureGPR = structure.gpr(); + } // We can skip all that comes next if we already have array storage. - MacroAssembler::JumpList slowCases = - jumpSlowForUnwantedArrayMode(tempGPR, desiredArrayMode); + MacroAssembler::JumpList done; + + if (node.op() == ArrayifyToStructure) { + done.append(m_jit.branchWeakPtr( + JITCompiler::Equal, + JITCompiler::Address(baseReg, JSCell::structureOffset()), + node.structure())); + } else { + m_jit.loadPtr( + MacroAssembler::Address(baseReg, JSCell::structureOffset()), structureGPR); - m_jit.loadPtr( - MacroAssembler::Address(baseReg, JSObject::butterflyOffset()), tempGPR); + m_jit.load8( + MacroAssembler::Address(structureGPR, Structure::indexingTypeOffset()), tempGPR); - MacroAssembler::Jump done = m_jit.jump(); + done = jumpSlowForUnwantedArrayMode(tempGPR, node.arrayMode(), true); + + // Next check that the object does not intercept indexed accesses. If it does, + // then this mode won't work. + speculationCheck( + BadIndexingType, JSValueSource::unboxedCell(baseReg), NoNode, + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero))); + } - slowCases.link(&m_jit); - // If we're allegedly creating contiguous storage and the index is bogus, then // just don't. - if (node.arrayMode() == Array::ToContiguous && propertyReg != InvalidGPRReg) { + if (node.arrayMode().type() == Array::Contiguous && propertyReg != InvalidGPRReg) { speculationCheck( Uncountable, JSValueRegs(), NoNode, m_jit.branch32( MacroAssembler::AboveOrEqual, propertyReg, TrustedImm32(MIN_SPARSE_ARRAY_INDEX))); } - // Next check that the object does not intercept indexed accesses. If it does, - // then this mode won't work. - speculationCheck( - BadIndexingType, JSValueSource::unboxedCell(baseReg), NoNode, - m_jit.branchTest8( - MacroAssembler::NonZero, - MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), - MacroAssembler::TrustedImm32(InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero))); - // Now call out to create the array storage. silentSpillAllRegisters(tempGPR); - switch (node.arrayMode()) { - case ALL_EFFECTFUL_CONTIGUOUS_MODES: + switch (node.arrayMode().type()) { + case Array::Contiguous: callOperation(operationEnsureContiguous, tempGPR, baseReg); break; - case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: callOperation(operationEnsureArrayStorage, tempGPR, baseReg); break; default: @@ -555,41 +550,42 @@ void SpeculativeJIT::arrayify(Node& node, GPRReg baseReg, GPRReg propertyReg) break; } silentFillAllRegisters(tempGPR); - - // Alas, we need to reload the structure because silent spilling does not save - // temporaries. Nor would it be useful for it to do so. Either way we're talking - // about a load. - m_jit.loadPtr( - MacroAssembler::Address(baseReg, JSCell::structureOffset()), structureGPR); - - // Finally, check that we have the kind of array storage that we wanted to get. - // Note that this is a backwards speculation check, which will result in the - // bytecode operation corresponding to this arrayification being reexecuted. - // That's fine, since arrayification is not user-visible. - m_jit.load8( - MacroAssembler::Address(structureGPR, Structure::indexingTypeOffset()), structureGPR); - speculationCheck( - BadIndexingType, JSValueSource::unboxedCell(baseReg), NoNode, - jumpSlowForUnwantedArrayMode(structureGPR, desiredArrayMode)); + + if (node.op() == ArrayifyToStructure) { + speculationCheck( + BadIndexingType, JSValueSource::unboxedCell(baseReg), NoNode, + m_jit.branchWeakPtr( + JITCompiler::NotEqual, + JITCompiler::Address(baseReg, JSCell::structureOffset()), + node.structure())); + } else { + // Alas, we need to reload the structure because silent spilling does not save + // temporaries. Nor would it be useful for it to do so. Either way we're talking + // about a load. + m_jit.loadPtr( + MacroAssembler::Address(baseReg, JSCell::structureOffset()), structureGPR); + + // Finally, check that we have the kind of array storage that we wanted to get. + // Note that this is a backwards speculation check, which will result in the + // bytecode operation corresponding to this arrayification being reexecuted. + // That's fine, since arrayification is not user-visible. + m_jit.load8( + MacroAssembler::Address(structureGPR, Structure::indexingTypeOffset()), structureGPR); + speculationCheck( + BadIndexingType, JSValueSource::unboxedCell(baseReg), NoNode, + jumpSlowForUnwantedArrayMode(structureGPR, node.arrayMode())); + } done.link(&m_jit); - storageResult(tempGPR, m_compileIndex); + noResult(m_compileIndex); } void SpeculativeJIT::arrayify(Node& node) { - ASSERT(modeIsSpecific(node.arrayMode())); + ASSERT(node.arrayMode().isSpecific()); SpeculateCellOperand base(this, node.child1()); - if (modeAlreadyChecked(m_state.forNode(node.child1()), node.arrayMode())) { - GPRTemporary temp(this); - m_jit.loadPtr( - MacroAssembler::Address(base.gpr(), JSObject::butterflyOffset()), temp.gpr()); - storageResult(temp.gpr(), m_compileIndex); - return; - } - if (!node.child2()) { arrayify(node, base.gpr(), InvalidGPRReg); return; @@ -1803,7 +1799,6 @@ ValueRecovery SpeculativeJIT::computeValueRecoveryFor(const ValueSource& valueSo void SpeculativeJIT::compileGetCharCodeAt(Node& node) { - ASSERT(node.child3() == NoNode); SpeculateCellOperand string(this, node.child1()); SpeculateStrictInt32Operand index(this, node.child2()); StorageOperand storage(this, node.child3()); @@ -1812,12 +1807,7 @@ void SpeculativeJIT::compileGetCharCodeAt(Node& node) GPRReg indexReg = index.gpr(); 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; - } + ASSERT(speculationChecked(m_state.forNode(node.child1()).m_type, SpecString)); // unsigned comparison so we can filter out negative indices and indices that are too large speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, indexReg, MacroAssembler::Address(stringReg, JSString::offsetOfLength()))); @@ -1851,7 +1841,7 @@ void SpeculativeJIT::compileGetByValOnString(Node& node) GPRReg propertyReg = property.gpr(); GPRReg storageReg = storage.gpr(); - ASSERT(modeAlreadyChecked(m_state.forNode(node.child1()), Array::String)); + ASSERT(ArrayMode(Array::String).alreadyChecked(m_state.forNode(node.child1()))); // unsigned comparison so we can filter out negative indices and indices that are too large speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSString::offsetOfLength()))); @@ -2260,7 +2250,7 @@ void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor& GPRTemporary result(this); GPRReg resultReg = result.gpr(); - ASSERT(modeAlreadyChecked(m_state.forNode(node.child1()), node.arrayMode())); + ASSERT(node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))); speculationCheck( Uncountable, JSValueRegs(), NoNode, @@ -2410,7 +2400,7 @@ void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor GPRReg propertyReg = property.gpr(); GPRReg storageReg = storage.gpr(); - ASSERT(modeAlreadyChecked(m_state.forNode(node.child1()), node.arrayMode())); + ASSERT(node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))); FPRTemporary result(this); FPRReg resultReg = result.fpr(); @@ -2426,7 +2416,7 @@ void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor case 8: { m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), resultReg); MacroAssembler::Jump notNaN = m_jit.branchDouble(MacroAssembler::DoubleEqual, resultReg, resultReg); - static const double NaN = std::numeric_limits<double>::quiet_NaN(); + static const double NaN = QNaN; m_jit.loadDouble(&NaN, resultReg); notNaN.link(&m_jit); break; @@ -2447,7 +2437,7 @@ void SpeculativeJIT::compilePutByValForFloatTypedArray(const TypedArrayDescripto SpeculateDoubleOperand valueOp(this, valueUse); - ASSERT_UNUSED(baseUse, modeAlreadyChecked(m_state.forNode(baseUse), node.arrayMode())); + ASSERT_UNUSED(baseUse, node.arrayMode().alreadyChecked(m_state.forNode(baseUse))); GPRTemporary result(this); @@ -3230,7 +3220,7 @@ void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node) const TypedArrayDescriptor* descriptor = typedArrayDescriptor(node.arrayMode()); - switch (node.arrayMode()) { + switch (node.arrayMode().type()) { case Array::String: m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), storageReg); @@ -3270,7 +3260,7 @@ void SpeculativeJIT::compileGetByValOnArguments(Node& node) if (!m_compileOkay) return; - ASSERT(modeAlreadyChecked(m_state.forNode(node.child1()), Array::Arguments)); + ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_state.forNode(node.child1()))); // Two really lame checks. speculationCheck( @@ -3327,7 +3317,7 @@ void SpeculativeJIT::compileGetArgumentsLength(Node& node) if (!m_compileOkay) return; - ASSERT(modeAlreadyChecked(m_state.forNode(node.child1()), Array::Arguments)); + ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_state.forNode(node.child1()))); speculationCheck( Uncountable, JSValueSource(), NoNode, @@ -3345,8 +3335,8 @@ void SpeculativeJIT::compileGetArrayLength(Node& node) { const TypedArrayDescriptor* descriptor = typedArrayDescriptor(node.arrayMode()); - switch (node.arrayMode()) { - case ARRAY_WITH_CONTIGUOUS_MODES: { + switch (node.arrayMode().type()) { + case Array::Contiguous: { StorageOperand storage(this, node.child2()); GPRTemporary result(this, storage); GPRReg storageReg = storage.gpr(); @@ -3356,8 +3346,8 @@ void SpeculativeJIT::compileGetArrayLength(Node& node) integerResult(resultReg, m_compileIndex); break; } - case ARRAY_WITH_ARRAY_STORAGE_MODES: - case ARRAY_EFFECTFUL_MODES: { + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: { StorageOperand storage(this, node.child2()); GPRTemporary result(this, storage); GPRReg storageReg = storage.gpr(); @@ -3542,7 +3532,7 @@ void SpeculativeJIT::compileReallocatePropertyStorage(Node& node) storageResult(scratchGPR2, m_compileIndex); } -GPRReg SpeculativeJIT::temporaryRegisterForPutByVal(GPRTemporary& temporary, Array::Mode arrayMode) +GPRReg SpeculativeJIT::temporaryRegisterForPutByVal(GPRTemporary& temporary, ArrayMode arrayMode) { if (!putByValWillNeedExtraRegister(arrayMode)) return InvalidGPRReg; diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index aadcdb06b..446ea7dbe 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -543,7 +543,7 @@ public: m_jit.move(valueOfJSConstantAsImm64(plan.nodeIndex()), plan.gpr()); break; case SetDoubleConstant: - m_jit.move(Imm64(valueOfNumberConstant(plan.nodeIndex())), canTrample); + m_jit.move(Imm64(reinterpretDoubleToInt64(valueOfNumberConstant(plan.nodeIndex()))), canTrample); m_jit.move64ToDouble(canTrample, plan.fpr()); break; case Load32PayloadBoxInt: @@ -2270,47 +2270,15 @@ public: void compileAllocatePropertyStorage(Node&); void compileReallocatePropertyStorage(Node&); -#if USE(JSVALUE64) - MacroAssembler::JumpList compileContiguousGetByVal(Node&, GPRReg baseReg, GPRReg propertyReg, GPRReg storageReg, GPRReg resultReg); - MacroAssembler::JumpList compileArrayStorageGetByVal(Node&, GPRReg baseReg, GPRReg propertyReg, GPRReg storageReg, GPRReg resultReg); -#else - MacroAssembler::JumpList compileContiguousGetByVal(Node&, GPRReg baseReg, GPRReg propertyReg, GPRReg storageReg, GPRReg resultTagReg, GPRReg resultPayloadReg); - MacroAssembler::JumpList compileArrayStorageGetByVal(Node&, GPRReg baseReg, GPRReg propertyReg, GPRReg storageReg, GPRReg resultTagReg, GPRReg resultPayloadReg); -#endif - - bool putByValWillNeedExtraRegister(Array::Mode arrayMode) + bool putByValWillNeedExtraRegister(ArrayMode arrayMode) { - switch (arrayMode) { - // For ArrayStorage, we need an extra reg for stores to holes except if - // we're in SlowPut mode. - case ARRAY_STORAGE_TO_HOLE_MODES: - case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: - case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: - return true; - - // For Contiguous, we need an extra reg for any access that may store - // to the tail. - case CONTIGUOUS_TO_TAIL_MODES: - case OUT_OF_BOUNDS_CONTIGUOUS_MODES: - case ALL_EFFECTFUL_CONTIGUOUS_MODES: - return true; - - default: - return false; - } + return arrayMode.mayStoreToHole(); } - GPRReg temporaryRegisterForPutByVal(GPRTemporary&, Array::Mode); + GPRReg temporaryRegisterForPutByVal(GPRTemporary&, ArrayMode); GPRReg temporaryRegisterForPutByVal(GPRTemporary& temporary, Node& node) { return temporaryRegisterForPutByVal(temporary, node.arrayMode()); } -#if USE(JSVALUE64) - MacroAssembler::JumpList compileContiguousPutByVal(Node&, GPRReg baseReg, GPRReg propertyReg, GPRReg storageReg, GPRReg valueReg, GPRReg tempReg); - MacroAssembler::JumpList compileArrayStoragePutByVal(Node&, GPRReg baseReg, GPRReg propertyReg, GPRReg storageReg, GPRReg valueReg, GPRReg tempReg); -#else - MacroAssembler::JumpList compileContiguousPutByVal(Node&, GPRReg baseReg, GPRReg propertyReg, GPRReg storageReg, GPRReg valueTagReg, GPRReg valuePayloadReg); - MacroAssembler::JumpList compileArrayStoragePutByVal(Node&, GPRReg baseReg, GPRReg propertyReg, GPRReg storageReg, GPRReg valueTagReg, GPRReg valuePayloadReg); -#endif void compileGetCharCodeAt(Node&); void compileGetByValOnString(Node&); @@ -2445,9 +2413,9 @@ public: JumpReplacementWatchpoint* forwardSpeculationWatchpoint(ExitKind = UncountableWatchpoint); JumpReplacementWatchpoint* speculationWatchpointWithConditionalDirection(ExitKind, bool isForward); - const TypedArrayDescriptor* typedArrayDescriptor(Array::Mode); + const TypedArrayDescriptor* typedArrayDescriptor(ArrayMode); - JITCompiler::JumpList jumpSlowForUnwantedArrayMode(GPRReg tempWithIndexingTypeReg, Array::Mode arrayMode); + JITCompiler::JumpList jumpSlowForUnwantedArrayMode(GPRReg tempWithIndexingTypeReg, ArrayMode, bool invert = false); void checkArray(Node&); void arrayify(Node&, GPRReg baseReg, GPRReg propertyReg); void arrayify(Node&); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index ab089ba36..65fdf5593 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -2046,113 +2046,6 @@ void SpeculativeJIT::emitBranch(Node& node) } } -MacroAssembler::JumpList SpeculativeJIT::compileContiguousGetByVal(Node&, GPRReg, GPRReg propertyReg, GPRReg storageReg, GPRReg resultTagReg, GPRReg resultPayloadReg) -{ - MacroAssembler::JumpList slowCases; - slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); - - m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTagReg); - m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayloadReg); - slowCases.append(m_jit.branch32(MacroAssembler::Equal, resultTagReg, TrustedImm32(JSValue::EmptyValueTag))); - - return slowCases; -} - -MacroAssembler::JumpList SpeculativeJIT::compileArrayStorageGetByVal(Node&, GPRReg, GPRReg propertyReg, GPRReg storageReg, GPRReg resultTagReg, GPRReg resultPayloadReg) -{ - MacroAssembler::Jump outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::vectorLengthOffset())); - - m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTagReg); - m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayloadReg); - MacroAssembler::Jump hole = m_jit.branch32(MacroAssembler::Equal, resultTagReg, TrustedImm32(JSValue::EmptyValueTag)); - - MacroAssembler::JumpList slowCases; - slowCases.append(outOfBounds); - slowCases.append(hole); - return slowCases; -} - -MacroAssembler::JumpList SpeculativeJIT::compileContiguousPutByVal(Node& node, GPRReg, GPRReg propertyReg, GPRReg storageReg, GPRReg valueTagReg, GPRReg valuePayloadReg) -{ - Array::Mode arrayMode = node.arrayMode(); - - MacroAssembler::JumpList slowCases; - - if (!mayStoreToTail(arrayMode)) { - speculationCheck( - Uncountable, JSValueRegs(), NoNode, - m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); - } else { - MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); - - slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfVectorLength()))); - - if (isInBoundsAccess(arrayMode)) - speculationCheck(Uncountable, JSValueRegs(), NoNode, slowCases); - - m_jit.add32(TrustedImm32(1), propertyReg); - m_jit.store32(propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); - m_jit.sub32(TrustedImm32(1), propertyReg); - - inBounds.link(&m_jit); - } - - m_jit.store32(valueTagReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); - m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); - - if (isInBoundsAccess(arrayMode)) - return MacroAssembler::JumpList(); - return slowCases; -} - -MacroAssembler::JumpList SpeculativeJIT::compileArrayStoragePutByVal(Node& node, GPRReg, GPRReg propertyReg, GPRReg storageReg, GPRReg valueTagReg, GPRReg valuePayloadReg) -{ - Array::Mode arrayMode = node.arrayMode(); - - MacroAssembler::JumpList slowCases; - - MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::vectorLengthOffset())); - if (isInBoundsAccess(arrayMode)) - speculationCheck(OutOfBounds, JSValueRegs(), NoNode, beyondArrayBounds); - else - slowCases.append(beyondArrayBounds); - - // Check if we're writing to a hole; if so increment m_numValuesInVector. - if (!mayStoreToHole(arrayMode)) { - // This is uncountable because if we take this exit, then the baseline JIT - // will immediately count the hole store. So there is no need for exit - // profiling. - speculationCheck( - Uncountable, JSValueRegs(), NoNode, - m_jit.branch32(MacroAssembler::Equal, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), TrustedImm32(JSValue::EmptyValueTag))); - } else { - MacroAssembler::Jump notHoleValue = m_jit.branch32(MacroAssembler::NotEqual, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), TrustedImm32(JSValue::EmptyValueTag)); - if (isSlowPutAccess(arrayMode)) { - // This is sort of strange. If we wanted to optimize this code path, we would invert - // the above branch. But it's simply not worth it since this only happens if we're - // already having a bad time. - slowCases.append(m_jit.jump()); - } else { - m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageReg, ArrayStorage::numValuesInVectorOffset())); - - // If we're writing to a hole we might be growing the array; - MacroAssembler::Jump lengthDoesNotNeedUpdate = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::lengthOffset())); - m_jit.add32(TrustedImm32(1), propertyReg); - m_jit.store32(propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::lengthOffset())); - m_jit.sub32(TrustedImm32(1), propertyReg); - - lengthDoesNotNeedUpdate.link(&m_jit); - } - notHoleValue.link(&m_jit); - } - - // Store the value to the array. - m_jit.store32(valueTagReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); - m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); - - return slowCases; -} - void SpeculativeJIT::compile(Node& node) { NodeType op = node.op(); @@ -2646,13 +2539,14 @@ void SpeculativeJIT::compile(Node& node) break; } - case Arrayify: { + case Arrayify: + case ArrayifyToStructure: { arrayify(node); break; } case GetByVal: { - switch (node.arrayMode()) { + switch (node.arrayMode().type()) { case Array::SelectUsingPredictions: case Array::ForceExit: ASSERT_NOT_REACHED(); @@ -2673,29 +2567,28 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); break; } - case IN_BOUNDS_CONTIGUOUS_MODES: { - SpeculateStrictInt32Operand property(this, node.child2()); - StorageOperand storage(this, node.child3()); + case Array::Contiguous: { + if (node.arrayMode().isInBounds()) { + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); - GPRReg propertyReg = property.gpr(); - GPRReg storageReg = storage.gpr(); + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); - if (!m_compileOkay) - return; + if (!m_compileOkay) + return; - speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); - GPRTemporary resultTag(this); - GPRTemporary resultPayload(this); - m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag.gpr()); - speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::Equal, resultTag.gpr(), TrustedImm32(JSValue::EmptyValueTag))); - m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr()); - jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); - break; - } - case CONTIGUOUS_TO_TAIL_MODES: - case OUT_OF_BOUNDS_CONTIGUOUS_MODES: - case ALL_EFFECTFUL_CONTIGUOUS_MODES: { + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag.gpr()); + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::Equal, resultTag.gpr(), TrustedImm32(JSValue::EmptyValueTag))); + m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr()); + jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); + break; + } + SpeculateCellOperand base(this, node.child1()); SpeculateStrictInt32Operand property(this, node.child2()); StorageOperand storage(this, node.child3()); @@ -2712,8 +2605,14 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultTagReg = resultTag.gpr(); GPRReg resultPayloadReg = resultPayload.gpr(); - MacroAssembler::JumpList slowCases = - compileContiguousGetByVal(node, baseReg, propertyReg, storageReg, resultTagReg, resultPayloadReg); + MacroAssembler::JumpList slowCases; + + slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + + m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTagReg); + m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayloadReg); + slowCases.append(m_jit.branch32(MacroAssembler::Equal, resultTagReg, TrustedImm32(JSValue::EmptyValueTag))); + addSlowPathGenerator( slowPathCall( slowCases, this, operationGetByValArrayInt, @@ -2722,30 +2621,30 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(resultTagReg, resultPayloadReg, m_compileIndex); break; } - case IN_BOUNDS_ARRAY_STORAGE_MODES: { - SpeculateStrictInt32Operand property(this, node.child2()); - StorageOperand storage(this, node.child3()); - GPRReg propertyReg = property.gpr(); - GPRReg storageReg = storage.gpr(); + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: { + if (node.arrayMode().isInBounds()) { + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); - if (!m_compileOkay) - return; + if (!m_compileOkay) + return; - speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::vectorLengthOffset()))); + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::vectorLengthOffset()))); - GPRTemporary resultTag(this); - GPRTemporary resultPayload(this); + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); - m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag.gpr()); - speculationCheck(OutOfBounds, 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()); + m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag.gpr()); + speculationCheck(OutOfBounds, 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()); - jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); - break; - } - case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: - case SLOW_PUT_ARRAY_STORAGE_MODES: - case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: { + jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); + break; + } + SpeculateCellOperand base(this, node.child1()); SpeculateStrictInt32Operand property(this, node.child2()); StorageOperand storage(this, node.child3()); @@ -2829,10 +2728,10 @@ void SpeculativeJIT::compile(Node& node) Edge child3 = m_jit.graph().varArgChild(node, 2); Edge child4 = m_jit.graph().varArgChild(node, 3); - Array::Mode arrayMode = modeForPut(node.arrayMode()); + ArrayMode arrayMode = node.arrayMode().modeForPut(); bool alreadyHandled = false; - switch (arrayMode) { + switch (arrayMode.type()) { case Array::SelectUsingPredictions: case Array::ForceExit: ASSERT_NOT_REACHED(); @@ -2871,9 +2770,8 @@ void SpeculativeJIT::compile(Node& node) GPRReg baseReg = base.gpr(); GPRReg propertyReg = property.gpr(); - switch (arrayMode) { - case ALL_CONTIGUOUS_MODES: - case ALL_EFFECTFUL_CONTIGUOUS_MODES: { + switch (arrayMode.type()) { + case Array::Contiguous: { JSValueOperand value(this, child3); GPRReg valueTagReg = value.tagGPR(); @@ -2900,16 +2798,36 @@ void SpeculativeJIT::compile(Node& node) break; } - MacroAssembler::JumpList slowCases = - compileContiguousPutByVal( - node, baseReg, propertyReg, storageReg, valueTagReg, valuePayloadReg); + MacroAssembler::JumpList slowCases; + if (arrayMode.isInBounds()) { + speculationCheck( + Uncountable, JSValueRegs(), NoNode, + m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + } else { + MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); + + slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfVectorLength()))); + + if (!arrayMode.isOutOfBounds()) + speculationCheck(Uncountable, JSValueRegs(), NoNode, slowCases); + + m_jit.add32(TrustedImm32(1), propertyReg); + m_jit.store32(propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); + m_jit.sub32(TrustedImm32(1), propertyReg); + + inBounds.link(&m_jit); + } + + m_jit.store32(valueTagReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + base.use(); property.use(); value.use(); storage.use(); - if (!slowCases.empty()) { + if (arrayMode.isOutOfBounds()) { addSlowPathGenerator( slowPathCall( slowCases, this, @@ -2921,8 +2839,8 @@ void SpeculativeJIT::compile(Node& node) break; } - case ALL_ARRAY_STORAGE_MODES: - case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: { + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: { JSValueOperand value(this, child3); GPRReg valueTagReg = value.tagGPR(); @@ -2950,9 +2868,46 @@ void SpeculativeJIT::compile(Node& node) break; } - MacroAssembler::JumpList slowCases = - compileArrayStoragePutByVal( - node, baseReg, propertyReg, storageReg, valueTagReg, valuePayloadReg); + MacroAssembler::JumpList slowCases; + + MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::vectorLengthOffset())); + if (!arrayMode.isOutOfBounds()) + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, beyondArrayBounds); + else + slowCases.append(beyondArrayBounds); + + // Check if we're writing to a hole; if so increment m_numValuesInVector. + if (arrayMode.isInBounds()) { + // This is uncountable because if we take this exit, then the baseline JIT + // will immediately count the hole store. So there is no need for exit + // profiling. + speculationCheck( + Uncountable, JSValueRegs(), NoNode, + m_jit.branch32(MacroAssembler::Equal, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), TrustedImm32(JSValue::EmptyValueTag))); + } else { + MacroAssembler::Jump notHoleValue = m_jit.branch32(MacroAssembler::NotEqual, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), TrustedImm32(JSValue::EmptyValueTag)); + if (arrayMode.isSlowPut()) { + // This is sort of strange. If we wanted to optimize this code path, we would invert + // the above branch. But it's simply not worth it since this only happens if we're + // already having a bad time. + slowCases.append(m_jit.jump()); + } else { + m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageReg, ArrayStorage::numValuesInVectorOffset())); + + // If we're writing to a hole we might be growing the array; + MacroAssembler::Jump lengthDoesNotNeedUpdate = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::lengthOffset())); + m_jit.add32(TrustedImm32(1), propertyReg); + m_jit.store32(propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::lengthOffset())); + m_jit.sub32(TrustedImm32(1), propertyReg); + + lengthDoesNotNeedUpdate.link(&m_jit); + } + notHoleValue.link(&m_jit); + } + + // Store the value to the array. + m_jit.store32(valueTagReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); base.use(); property.use(); @@ -3070,7 +3025,7 @@ void SpeculativeJIT::compile(Node& node) } case ArrayPush: { - ASSERT(modeIsJSArray(node.arrayMode())); + ASSERT(node.arrayMode().isJSArray()); SpeculateCellOperand base(this, node.child1()); JSValueOperand value(this, node.child2()); @@ -3089,9 +3044,8 @@ void SpeculativeJIT::compile(Node& node) StorageOperand storage(this, node.child3()); GPRReg storageGPR = storage.gpr(); - switch (node.arrayMode()) { - case Array::ArrayWithContiguous: - case Array::ArrayWithContiguousOutOfBounds: { + switch (node.arrayMode().type()) { + case Array::Contiguous: { m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); m_jit.store32(valueTagGPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); @@ -3110,8 +3064,7 @@ void SpeculativeJIT::compile(Node& node) break; } - case Array::ArrayWithArrayStorage: - case Array::ArrayWithArrayStorageOutOfBounds: { + case Array::ArrayStorage: { m_jit.load32(MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset()), storageLengthGPR); // Refuse to handle bizarre lengths. @@ -3141,7 +3094,7 @@ void SpeculativeJIT::compile(Node& node) } case ArrayPop: { - ASSERT(modeIsJSArray(node.arrayMode())); + ASSERT(node.arrayMode().isJSArray()); SpeculateCellOperand base(this, node.child1()); StorageOperand storage(this, node.child2()); @@ -3153,9 +3106,8 @@ void SpeculativeJIT::compile(Node& node) GPRReg valuePayloadGPR = valuePayload.gpr(); GPRReg storageGPR = storage.gpr(); - switch (node.arrayMode()) { - case Array::ArrayWithContiguous: - case Array::ArrayWithContiguousOutOfBounds: { + switch (node.arrayMode().type()) { + case Array::Contiguous: { m_jit.load32( MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), valuePayloadGPR); MacroAssembler::Jump undefinedCase = @@ -3188,8 +3140,7 @@ void SpeculativeJIT::compile(Node& node) break; } - case Array::ArrayWithArrayStorage: - case Array::ArrayWithArrayStorageOutOfBounds: { + case Array::ArrayStorage: { GPRTemporary storageLength(this); GPRReg storageLengthGPR = storageLength.gpr(); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index f11fd1663..6c066c388 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -2111,107 +2111,6 @@ void SpeculativeJIT::emitBranch(Node& node) } } -MacroAssembler::JumpList SpeculativeJIT::compileContiguousGetByVal(Node&, GPRReg, GPRReg propertyReg, GPRReg storageReg, GPRReg resultReg) -{ - MacroAssembler::JumpList slowCases; - slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); - - m_jit.load64(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), resultReg); - slowCases.append(m_jit.branchTest64(MacroAssembler::Zero, resultReg)); - - return slowCases; -} - -MacroAssembler::JumpList SpeculativeJIT::compileArrayStorageGetByVal(Node&, GPRReg, GPRReg propertyReg, GPRReg storageReg, GPRReg resultReg) -{ - MacroAssembler::Jump outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::vectorLengthOffset())); - - m_jit.load64(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), resultReg); - MacroAssembler::Jump hole = m_jit.branchTest64(MacroAssembler::Zero, resultReg); - - MacroAssembler::JumpList slowCases; - slowCases.append(outOfBounds); - slowCases.append(hole); - return slowCases; -} - -MacroAssembler::JumpList SpeculativeJIT::compileContiguousPutByVal(Node& node, GPRReg, GPRReg propertyReg, GPRReg storageReg, GPRReg valueReg, GPRReg tempReg) -{ - Array::Mode arrayMode = node.arrayMode(); - - MacroAssembler::JumpList slowCases; - - if (!mayStoreToTail(arrayMode)) { - speculationCheck( - Uncountable, JSValueRegs(), NoNode, - m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); - } else { - MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); - - slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfVectorLength()))); - - if (isInBoundsAccess(arrayMode)) - speculationCheck(Uncountable, JSValueRegs(), NoNode, slowCases); - - m_jit.add32(TrustedImm32(1), propertyReg, tempReg); - m_jit.store32(tempReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); - - inBounds.link(&m_jit); - } - - m_jit.store64(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight)); - - if (isInBoundsAccess(arrayMode)) - return MacroAssembler::JumpList(); - return slowCases; -} - -MacroAssembler::JumpList SpeculativeJIT::compileArrayStoragePutByVal(Node& node, GPRReg, GPRReg propertyReg, GPRReg storageReg, GPRReg valueReg, GPRReg tempReg) -{ - Array::Mode arrayMode = node.arrayMode(); - - MacroAssembler::JumpList slowCases; - - MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::vectorLengthOffset())); - if (isInBoundsAccess(arrayMode)) - speculationCheck(OutOfBounds, JSValueRegs(), NoNode, beyondArrayBounds); - else - slowCases.append(beyondArrayBounds); - - // Check if we're writing to a hole; if so increment m_numValuesInVector. - if (!mayStoreToHole(arrayMode)) { - // This is uncountable because if we take this exit, then the baseline JIT - // will immediately count the hole store. So there is no need for exit - // profiling. - speculationCheck( - Uncountable, JSValueRegs(), NoNode, - m_jit.branchTest64(MacroAssembler::Zero, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])))); - } else { - MacroAssembler::Jump notHoleValue = m_jit.branchTest64(MacroAssembler::NonZero, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); - if (isSlowPutAccess(arrayMode)) { - // This is sort of strange. If we wanted to optimize this code path, we would invert - // the above branch. But it's simply not worth it since this only happens if we're - // already having a bad time. - slowCases.append(m_jit.jump()); - } else { - m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageReg, ArrayStorage::numValuesInVectorOffset())); - - // If we're writing to a hole we might be growing the array; - MacroAssembler::Jump lengthDoesNotNeedUpdate = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::lengthOffset())); - m_jit.add32(TrustedImm32(1), propertyReg, tempReg); - m_jit.store32(tempReg, MacroAssembler::Address(storageReg, ArrayStorage::lengthOffset())); - - lengthDoesNotNeedUpdate.link(&m_jit); - } - notHoleValue.link(&m_jit); - } - - // Store the value to the array. - m_jit.store64(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); - - return slowCases; -} - void SpeculativeJIT::compile(Node& node) { NodeType op = node.op(); @@ -2673,13 +2572,14 @@ void SpeculativeJIT::compile(Node& node) break; } - case Arrayify: { + case Arrayify: + case ArrayifyToStructure: { arrayify(node); break; } case GetByVal: { - switch (node.arrayMode()) { + switch (node.arrayMode().type()) { case Array::SelectUsingPredictions: case Array::ForceExit: ASSERT_NOT_REACHED(); @@ -2698,27 +2598,26 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(result.gpr(), m_compileIndex); break; } - case IN_BOUNDS_CONTIGUOUS_MODES: { - SpeculateStrictInt32Operand property(this, node.child2()); - StorageOperand storage(this, node.child3()); - - GPRReg propertyReg = property.gpr(); - GPRReg storageReg = storage.gpr(); - - if (!m_compileOkay) - return; - - speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + case Array::Contiguous: { + if (node.arrayMode().isInBounds()) { + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); + + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); + + if (!m_compileOkay) + return; + + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + + GPRTemporary result(this); + m_jit.load64(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.gpr()); + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchTest64(MacroAssembler::Zero, result.gpr())); + jsValueResult(result.gpr(), m_compileIndex); + break; + } - GPRTemporary result(this); - m_jit.load64(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.gpr()); - speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchTest64(MacroAssembler::Zero, result.gpr())); - jsValueResult(result.gpr(), m_compileIndex); - break; - } - case CONTIGUOUS_TO_TAIL_MODES: - case OUT_OF_BOUNDS_CONTIGUOUS_MODES: - case ALL_EFFECTFUL_CONTIGUOUS_MODES: { SpeculateCellOperand base(this, node.child1()); SpeculateStrictInt32Operand property(this, node.child2()); StorageOperand storage(this, node.child3()); @@ -2733,8 +2632,13 @@ void SpeculativeJIT::compile(Node& node) GPRTemporary result(this); GPRReg resultReg = result.gpr(); - MacroAssembler::JumpList slowCases = - compileContiguousGetByVal(node, baseReg, propertyReg, storageReg, resultReg); + MacroAssembler::JumpList slowCases; + + slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + + m_jit.load64(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), resultReg); + slowCases.append(m_jit.branchTest64(MacroAssembler::Zero, resultReg)); + addSlowPathGenerator( slowPathCall( slowCases, this, operationGetByValArrayInt, @@ -2743,28 +2647,28 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(resultReg, m_compileIndex); break; } - case IN_BOUNDS_ARRAY_STORAGE_MODES: { - SpeculateStrictInt32Operand property(this, node.child2()); - StorageOperand storage(this, node.child3()); + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: { + if (node.arrayMode().isInBounds()) { + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); - GPRReg propertyReg = property.gpr(); - GPRReg storageReg = storage.gpr(); + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); - if (!m_compileOkay) - return; + if (!m_compileOkay) + return; - speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::vectorLengthOffset()))); + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::vectorLengthOffset()))); - GPRTemporary result(this); - m_jit.load64(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), result.gpr()); - speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchTest64(MacroAssembler::Zero, result.gpr())); + GPRTemporary result(this); + m_jit.load64(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), result.gpr()); + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchTest64(MacroAssembler::Zero, result.gpr())); - jsValueResult(result.gpr(), m_compileIndex); - break; - } - case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: - case SLOW_PUT_ARRAY_STORAGE_MODES: - case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: { + jsValueResult(result.gpr(), m_compileIndex); + break; + } + SpeculateCellOperand base(this, node.child1()); SpeculateStrictInt32Operand property(this, node.child2()); StorageOperand storage(this, node.child3()); @@ -2779,8 +2683,13 @@ void SpeculativeJIT::compile(Node& node) GPRTemporary result(this); GPRReg resultReg = result.gpr(); - MacroAssembler::JumpList slowCases = - compileArrayStorageGetByVal(node, baseReg, propertyReg, storageReg, resultReg); + MacroAssembler::JumpList slowCases; + + slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::vectorLengthOffset()))); + + m_jit.load64(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), resultReg); + slowCases.append(m_jit.branchTest64(MacroAssembler::Zero, resultReg)); + addSlowPathGenerator( slowPathCall( slowCases, this, operationGetByValArrayInt, @@ -2836,10 +2745,10 @@ void SpeculativeJIT::compile(Node& node) Edge child3 = m_jit.graph().varArgChild(node, 2); Edge child4 = m_jit.graph().varArgChild(node, 3); - Array::Mode arrayMode = modeForPut(node.arrayMode()); + ArrayMode arrayMode = node.arrayMode().modeForPut(); bool alreadyHandled = false; - switch (arrayMode) { + switch (arrayMode.type()) { case Array::SelectUsingPredictions: case Array::ForceExit: ASSERT_NOT_REACHED(); @@ -2879,9 +2788,8 @@ void SpeculativeJIT::compile(Node& node) GPRReg baseReg = base.gpr(); GPRReg propertyReg = property.gpr(); - switch (arrayMode) { - case ALL_CONTIGUOUS_MODES: - case ALL_EFFECTFUL_CONTIGUOUS_MODES: { + switch (arrayMode.type()) { + case Array::Contiguous: { JSValueOperand value(this, child3); GPRReg valueReg = value.gpr(); @@ -2910,16 +2818,34 @@ void SpeculativeJIT::compile(Node& node) GPRTemporary temporary; GPRReg temporaryReg = temporaryRegisterForPutByVal(temporary, node); - MacroAssembler::JumpList slowCases = - compileContiguousPutByVal( - node, baseReg, propertyReg, storageReg, valueReg, temporaryReg); + MacroAssembler::JumpList slowCases; + + if (arrayMode.isInBounds()) { + speculationCheck( + Uncountable, JSValueRegs(), NoNode, + m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + } else { + MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); + + slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfVectorLength()))); + + if (!arrayMode.isOutOfBounds()) + speculationCheck(Uncountable, JSValueRegs(), NoNode, slowCases); + + m_jit.add32(TrustedImm32(1), propertyReg, temporaryReg); + m_jit.store32(temporaryReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); + + inBounds.link(&m_jit); + } + + m_jit.store64(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight)); base.use(); property.use(); value.use(); storage.use(); - if (!slowCases.empty()) { + if (arrayMode.isOutOfBounds()) { addSlowPathGenerator( slowPathCall( slowCases, this, @@ -2931,8 +2857,8 @@ void SpeculativeJIT::compile(Node& node) break; } - case ALL_ARRAY_STORAGE_MODES: - case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: { + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: { JSValueOperand value(this, child3); GPRReg valueReg = value.gpr(); @@ -2961,9 +2887,44 @@ void SpeculativeJIT::compile(Node& node) GPRTemporary temporary; GPRReg temporaryReg = temporaryRegisterForPutByVal(temporary, node); - MacroAssembler::JumpList slowCases = - compileArrayStoragePutByVal( - node, baseReg, propertyReg, storageReg, valueReg, temporaryReg); + MacroAssembler::JumpList slowCases; + + MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::vectorLengthOffset())); + if (!arrayMode.isOutOfBounds()) + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, beyondArrayBounds); + else + slowCases.append(beyondArrayBounds); + + // Check if we're writing to a hole; if so increment m_numValuesInVector. + if (arrayMode.isInBounds()) { + // This is uncountable because if we take this exit, then the baseline JIT + // will immediately count the hole store. So there is no need for exit + // profiling. + speculationCheck( + Uncountable, JSValueRegs(), NoNode, + m_jit.branchTest64(MacroAssembler::Zero, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])))); + } else { + MacroAssembler::Jump notHoleValue = m_jit.branchTest64(MacroAssembler::NonZero, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); + if (arrayMode.isSlowPut()) { + // This is sort of strange. If we wanted to optimize this code path, we would invert + // the above branch. But it's simply not worth it since this only happens if we're + // already having a bad time. + slowCases.append(m_jit.jump()); + } else { + m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageReg, ArrayStorage::numValuesInVectorOffset())); + + // If we're writing to a hole we might be growing the array; + MacroAssembler::Jump lengthDoesNotNeedUpdate = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, ArrayStorage::lengthOffset())); + m_jit.add32(TrustedImm32(1), propertyReg, temporaryReg); + m_jit.store32(temporaryReg, MacroAssembler::Address(storageReg, ArrayStorage::lengthOffset())); + + lengthDoesNotNeedUpdate.link(&m_jit); + } + notHoleValue.link(&m_jit); + } + + // Store the value to the array. + m_jit.store64(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); base.use(); property.use(); @@ -3117,7 +3078,7 @@ void SpeculativeJIT::compile(Node& node) } case ArrayPush: { - ASSERT(modeIsJSArray(node.arrayMode())); + ASSERT(node.arrayMode().isJSArray()); SpeculateCellOperand base(this, node.child1()); JSValueOperand value(this, node.child2()); @@ -3135,9 +3096,8 @@ void SpeculativeJIT::compile(Node& node) StorageOperand storage(this, node.child3()); GPRReg storageGPR = storage.gpr(); - switch (node.arrayMode()) { - case Array::ArrayWithContiguous: - case Array::ArrayWithContiguousOutOfBounds: { + switch (node.arrayMode().type()) { + case Array::Contiguous: { m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); m_jit.store64(valueGPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight)); @@ -3154,8 +3114,7 @@ void SpeculativeJIT::compile(Node& node) break; } - case Array::ArrayWithArrayStorage: - case Array::ArrayWithArrayStorageOutOfBounds: { + case Array::ArrayStorage: { m_jit.load32(MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset()), storageLengthGPR); // Refuse to handle bizarre lengths. @@ -3187,7 +3146,7 @@ void SpeculativeJIT::compile(Node& node) } case ArrayPop: { - ASSERT(modeIsJSArray(node.arrayMode())); + ASSERT(node.arrayMode().isJSArray()); SpeculateCellOperand base(this, node.child1()); StorageOperand storage(this, node.child2()); @@ -3199,9 +3158,8 @@ void SpeculativeJIT::compile(Node& node) GPRReg valueGPR = value.gpr(); GPRReg storageLengthGPR = storageLength.gpr(); - switch (node.arrayMode()) { - case Array::ArrayWithContiguous: - case Array::ArrayWithContiguousOutOfBounds: { + switch (node.arrayMode().type()) { + case Array::Contiguous: { m_jit.load32( MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); MacroAssembler::Jump undefinedCase = @@ -3230,8 +3188,7 @@ void SpeculativeJIT::compile(Node& node) break; } - case Array::ArrayWithArrayStorage: - case Array::ArrayWithArrayStorageOutOfBounds: { + case Array::ArrayStorage: { m_jit.load32(MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset()), storageLengthGPR); JITCompiler::Jump undefinedCase = @@ -3873,9 +3830,15 @@ void SpeculativeJIT::compile(Node& node) ASSERT(node.structureSet().size()); + ExitKind exitKind; + if (m_jit.graph()[node.child1()].op() == WeakJSConstant) + exitKind = BadWeakConstantCache; + else + exitKind = BadCache; + if (node.structureSet().size() == 1) { speculationCheckWithConditionalDirection( - BadCache, JSValueRegs(base.gpr()), NoNode, + exitKind, JSValueRegs(base.gpr()), NoNode, m_jit.branchWeakPtr( JITCompiler::NotEqual, JITCompiler::Address(base.gpr(), JSCell::structureOffset()), @@ -3892,7 +3855,7 @@ void SpeculativeJIT::compile(Node& node) done.append(m_jit.branchWeakPtr(JITCompiler::Equal, structure.gpr(), node.structureSet()[i])); speculationCheckWithConditionalDirection( - BadCache, JSValueRegs(base.gpr()), NoNode, + exitKind, JSValueRegs(base.gpr()), NoNode, m_jit.branchWeakPtr( JITCompiler::NotEqual, structure.gpr(), node.structureSet().last()), node.op() == ForwardCheckStructure); @@ -3916,7 +3879,8 @@ void SpeculativeJIT::compile(Node& node) m_jit.addWeakReference(node.structure()); node.structure()->addTransitionWatchpoint( speculationWatchpointWithConditionalDirection( - BadCache, node.op() == ForwardStructureTransitionWatchpoint)); + m_jit.graph()[node.child1()].op() == WeakJSConstant ? BadWeakConstantCache : BadCache, + node.op() == ForwardStructureTransitionWatchpoint)); #if !ASSERT_DISABLED SpeculateCellOperand op1(this, node.child1()); |