diff options
Diffstat (limited to 'Source/JavaScriptCore')
40 files changed, 801 insertions, 216 deletions
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog index 76a90cad0..243e2a887 100644 --- a/Source/JavaScriptCore/ChangeLog +++ b/Source/JavaScriptCore/ChangeLog @@ -1,3 +1,264 @@ +2012-09-19 Geoffrey Garen <ggaren@apple.com> + + OSR exit sometimes neglects to create the arguments object + https://bugs.webkit.org/show_bug.cgi?id=97162 + + Reviewed by Filip Pizlo. + + No performance change. + + I don't know of any case where this is a real problem in TOT, but it + will become a problem if we start compiling eval, with, or catch, and/or + sometimes stop doing arguments optimizations in the bytecode. + + * dfg/DFGArgumentsSimplificationPhase.cpp: + (JSC::DFG::ArgumentsSimplificationPhase::run): Account for a + CreateArguments that has transformed into PhantomArguments. We used to + clear our reference to the CreateArguments node, but now we hold onto it, + so we need to account for it transforming. + + Don't replace a SetLocal(CreateArguments) with a SetLocal(JSValue()) + because that doesn't leave enough information behind for OSR exit to do + the right thing. Instead, maintain our reference to CreateArguments, and + rely on CreateArguments transforming into PhantomArguments after + optimization. SetLocal(PhantomArguments) is efficient, and it's a marker + for OSR exit to create the arguments object. + + Don't ASSERT that all PhantomArguments are unreferenced because we now + leave them in the graph as SetLocal(PhantomArguments), and that's harmless. + + * dfg/DFGArgumentsSimplificationPhase.h: + (NullableHashTraits): + (JSC::DFG::NullableHashTraits::emptyValue): Export our special hash table + for inline call frames so the OSR exit compiler can use it. + + * dfg/DFGOSRExitCompiler32_64.cpp: + (JSC::DFG::OSRExitCompiler::compileExit): + * dfg/DFGOSRExitCompiler64.cpp: + (JSC::DFG::OSRExitCompiler::compileExit): Don't load the 'arguments' + register to decide if we need to create the arguments object. Optimization + may have eliminated the initializing store to this register, in which + case we'll load garbage. Instead, use the global knowledge that all call + frames that optimized out 'arguments' now need to create it, and use a hash + table to make sure we do so only once per call frame. + + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::compile): SetLocal(PhantomArguments) is unique + because we haven't just changed a value's format or elided a load or store; + instead, we've replaced an object with JSValue(). We could try to account + for this in a general way, but for now it's a special-case optimization, + so we give it a specific OSR hint instead. + +2012-09-19 Filip Pizlo <fpizlo@apple.com> + + REGRESSION(r128802): It made some JS tests crash + https://bugs.webkit.org/show_bug.cgi?id=97001 + + Reviewed by Mark Hahnenberg. + + * runtime/JSGlobalObject.cpp: + (JSC::JSGlobalObject::visitChildren): + +2012-09-19 Filip Pizlo <fpizlo@apple.com> + + DFG should not assume that a ByVal access is generic just because it was unprofiled + https://bugs.webkit.org/show_bug.cgi?id=97088 + + Reviewed by Geoffrey Garen. + + We were not disambiguating between "Undecided" in the sense that the array profile + has no useful information versus "Undecided" in the sense that the array profile + knows that the access has not executed. That's an important distinction, since + the former form of "Undecided" means that we should consult value profiling, while + the latter means that we should force exit unless the value profiling indicates + that the access must be generic (base is not cell or property is not int). + + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::execute): + * dfg/DFGArrayMode.cpp: + (JSC::DFG::fromObserved): + (JSC::DFG::refineArrayMode): + (JSC::DFG::modeAlreadyChecked): + (JSC::DFG::modeToString): + * dfg/DFGArrayMode.h: + (JSC::DFG::canCSEStorage): + (JSC::DFG::modeIsSpecific): + (JSC::DFG::modeSupportsLength): + (JSC::DFG::benefitsFromStructureCheck): + +2012-09-19 Filip Pizlo <fpizlo@apple.com> + + DFG should not emit PutByVal hole case unless it has to + https://bugs.webkit.org/show_bug.cgi?id=97080 + + Reviewed by Geoffrey Garen. + + This causes us to generate less code for typical PutByVal's. But if profiling tells us + that the hole case is being hit, we generate the same code as we would have generated + before. This seems like a slight speed-up across the board. + + * assembler/MacroAssemblerARMv7.h: + (JSC::MacroAssemblerARMv7::store8): + (MacroAssemblerARMv7): + * assembler/MacroAssemblerX86.h: + (MacroAssemblerX86): + (JSC::MacroAssemblerX86::store8): + * assembler/MacroAssemblerX86_64.h: + (MacroAssemblerX86_64): + (JSC::MacroAssemblerX86_64::store8): + * assembler/X86Assembler.h: + (X86Assembler): + (JSC::X86Assembler::movb_i8m): + * bytecode/ArrayProfile.h: + (JSC::ArrayProfile::ArrayProfile): + (JSC::ArrayProfile::addressOfMayStoreToHole): + (JSC::ArrayProfile::mayStoreToHole): + (ArrayProfile): + * dfg/DFGArrayMode.cpp: + (JSC::DFG::fromObserved): + (JSC::DFG::modeAlreadyChecked): + (JSC::DFG::modeToString): + * dfg/DFGArrayMode.h: + (DFG): + (JSC::DFG::mayStoreToHole): + * dfg/DFGSpeculativeJIT32_64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * jit/JIT.h: + (JIT): + * jit/JITInlineMethods.h: + (JSC::JIT::emitArrayProfileStoreToHoleSpecialCase): + (JSC): + * jit/JITPropertyAccess.cpp: + (JSC::JIT::emit_op_put_by_val): + * jit/JITPropertyAccess32_64.cpp: + (JSC::JIT::emit_op_put_by_val): + * llint/LowLevelInterpreter32_64.asm: + * llint/LowLevelInterpreter64.asm: + +2012-09-18 Filip Pizlo <fpizlo@apple.com> + + DFG should not call out to C++ every time that it tries to put to an object that doesn't yet have array storage + https://bugs.webkit.org/show_bug.cgi?id=96983 + + Reviewed by Oliver Hunt. + + Introduce more polymorphism into the DFG's array mode support. Use that to + introduce the notion of effectul array modes, where the check for the mode + will perform actions necessary to ensure that we have the mode we want, if + the object is not already in that mode. Also added profiling support for + checking if an object is of a type that would not allow us to create array + storage (like a typed array or a string for example). + + This is a ~2x speed-up on loops that transform an object that did not have + indexed storage into one that does. + + * JSCTypedArrayStubs.h: + (JSC): + * bytecode/ArrayProfile.cpp: + (JSC::ArrayProfile::computeUpdatedPrediction): + * bytecode/ArrayProfile.h: + (JSC::ArrayProfile::ArrayProfile): + (JSC::ArrayProfile::mayInterceptIndexedAccesses): + (ArrayProfile): + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::execute): + * dfg/DFGArrayMode.cpp: + (JSC::DFG::fromObserved): + (DFG): + (JSC::DFG::modeAlreadyChecked): + (JSC::DFG::modeToString): + * dfg/DFGArrayMode.h: + (DFG): + (JSC::DFG::modeUsesButterfly): + (JSC::DFG::isSlowPutAccess): + (JSC::DFG::benefitsFromStructureCheck): + (JSC::DFG::isEffectful): + * dfg/DFGByteCodeParser.cpp: + (JSC::DFG::ByteCodeParser::getArrayMode): + (JSC::DFG::ByteCodeParser::getArrayModeAndEmitChecks): + (JSC::DFG::ByteCodeParser::parseBlock): + * dfg/DFGCSEPhase.cpp: + (JSC::DFG::CSEPhase::getPropertyStorageLoadElimination): + * dfg/DFGFixupPhase.cpp: + (JSC::DFG::FixupPhase::fixupNode): + (JSC::DFG::FixupPhase::checkArray): + * dfg/DFGGraph.h: + (JSC::DFG::Graph::byValIsPure): + * dfg/DFGNode.h: + (JSC::DFG::Node::hasArrayMode): + * dfg/DFGNodeType.h: + (DFG): + * dfg/DFGOperations.cpp: + * dfg/DFGOperations.h: + * dfg/DFGPredictionPropagationPhase.cpp: + (JSC::DFG::PredictionPropagationPhase::propagate): + * dfg/DFGSpeculativeJIT.cpp: + (JSC::DFG::SpeculativeJIT::checkArray): + (JSC::DFG::SpeculativeJIT::arrayify): + (DFG): + * dfg/DFGSpeculativeJIT.h: + (SpeculativeJIT): + * dfg/DFGSpeculativeJIT32_64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * runtime/Arguments.h: + (Arguments): + * runtime/JSNotAnObject.h: + (JSNotAnObject): + * runtime/JSObject.h: + (JSObject): + (JSC::JSObject::ensureArrayStorage): + * runtime/JSString.h: + (JSC::JSString::createStructure): + +2012-09-18 Filip Pizlo <fpizlo@apple.com> + + Include PhantomArguments in DFGDisassembly + https://bugs.webkit.org/show_bug.cgi?id=97043 + + Reviewed by Geoffrey Garen. + + * dfg/DFGNode.h: + (JSC::DFG::Node::willHaveCodeGenOrOSR): + +2012-09-18 Filip Pizlo <fpizlo@apple.com> + + REGRESSION(r128802): It made some JS tests crash + https://bugs.webkit.org/show_bug.cgi?id=97001 + + Reviewed by Mark Hahnenberg. + + IndexingHeaderInlineMethods was incorrectly assuming that if the HasArrayStorage bit is clear, then that means that indexing payload capacity is zero. + + * runtime/IndexingHeaderInlineMethods.h: + (JSC::IndexingHeader::preCapacity): + (JSC::IndexingHeader::indexingPayloadSizeInBytes): + +2012-09-18 Mark Hahnenberg <mhahnenberg@apple.com> + + Use WTF::HasTrivialDestructor instead of compiler-specific versions in JSC::NeedsDestructor + https://bugs.webkit.org/show_bug.cgi?id=96980 + + Reviewed by Benjamin Poulain. + + * runtime/JSCell.h: + (JSC): + (NeedsDestructor): + +2012-09-18 Filip Pizlo <fpizlo@apple.com> + + DFGOperations doesn't use NativeCallFrameTracer in enough places + https://bugs.webkit.org/show_bug.cgi?id=96987 + + Reviewed by Mark Hahnenberg. + + Anything that can GC should use it. + + * dfg/DFGOperations.cpp: + 2012-09-18 Mark Lam <mark.lam@apple.com> Not reviewed. Attempt at greening the WinCairo bot. Touching diff --git a/Source/JavaScriptCore/JSCTypedArrayStubs.h b/Source/JavaScriptCore/JSCTypedArrayStubs.h index fa42d3c56..e3926338c 100644 --- a/Source/JavaScriptCore/JSCTypedArrayStubs.h +++ b/Source/JavaScriptCore/JSCTypedArrayStubs.h @@ -74,7 +74,7 @@ public: \ protected:\ JS##name##Array(JSC::Structure*, JSGlobalObject*, PassRefPtr<name##Array>);\ void finishCreation(JSC::JSGlobalData&);\ - static const unsigned StructureFlags = JSC::OverridesGetPropertyNames | JSC::OverridesGetOwnPropertySlot | Base::StructureFlags;\ + static const unsigned StructureFlags = JSC::OverridesGetPropertyNames | JSC::InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | JSC::OverridesGetOwnPropertySlot | Base::StructureFlags; \ JSC::JSValue getByIndex(JSC::ExecState*, unsigned index);\ void indexSetter(JSC::ExecState*, unsigned index, JSC::JSValue);\ };\ diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h b/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h index 1dfe888d3..09a88fdda 100644 --- a/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h +++ b/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h @@ -737,6 +737,12 @@ public: store8(src, setupArmAddress(address)); } + void store8(RegisterID src, void* address) + { + move(TrustedImmPtr(address), addressTempRegister); + store8(src, ArmAddress(addressTempRegister, 0)); + } + void store16(RegisterID src, BaseIndex address) { store16(src, setupArmAddress(address)); diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerX86.h b/Source/JavaScriptCore/assembler/MacroAssemblerX86.h index d46867ae3..f6e373d3e 100644 --- a/Source/JavaScriptCore/assembler/MacroAssemblerX86.h +++ b/Source/JavaScriptCore/assembler/MacroAssemblerX86.h @@ -44,6 +44,7 @@ public: using MacroAssemblerX86Common::or32; using MacroAssemblerX86Common::load32; using MacroAssemblerX86Common::store32; + using MacroAssemblerX86Common::store8; using MacroAssemblerX86Common::branch32; using MacroAssemblerX86Common::call; using MacroAssemblerX86Common::jump; @@ -132,6 +133,12 @@ public: m_assembler.movl_rm(src, address); } + void store8(TrustedImm32 imm, void* address) + { + ASSERT(-128 <= imm.m_value && imm.m_value < 128); + m_assembler.movb_i8m(imm.m_value, address); + } + Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) { m_assembler.addl_im(imm.m_value, dest.m_ptr); diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h b/Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h index c827e8ae9..6493b0c34 100644 --- a/Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h +++ b/Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h @@ -45,6 +45,7 @@ public: using MacroAssemblerX86Common::sub32; using MacroAssemblerX86Common::load32; using MacroAssemblerX86Common::store32; + using MacroAssemblerX86Common::store8; using MacroAssemblerX86Common::call; using MacroAssemblerX86Common::jump; using MacroAssemblerX86Common::addDouble; @@ -114,6 +115,12 @@ public: move(TrustedImmPtr(address), scratchRegister); store32(imm, scratchRegister); } + + void store8(TrustedImm32 imm, void* address) + { + move(TrustedImmPtr(address), scratchRegister); + store8(imm, Address(scratchRegister)); + } Call call() { diff --git a/Source/JavaScriptCore/assembler/X86Assembler.h b/Source/JavaScriptCore/assembler/X86Assembler.h index acd573049..ecb178e88 100644 --- a/Source/JavaScriptCore/assembler/X86Assembler.h +++ b/Source/JavaScriptCore/assembler/X86Assembler.h @@ -1155,6 +1155,15 @@ public: m_formatter.immediate32(imm); } +#if !CPU(X86_64) + void movb_i8m(int imm, const void* addr) + { + ASSERT(-128 <= imm && imm < 128); + m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, addr); + m_formatter.immediate8(imm); + } +#endif + void movb_i8m(int imm, int offset, RegisterID base) { ASSERT(-128 <= imm && imm < 128); diff --git a/Source/JavaScriptCore/bytecode/ArrayProfile.cpp b/Source/JavaScriptCore/bytecode/ArrayProfile.cpp index 9f4e1ce20..3ba974d74 100644 --- a/Source/JavaScriptCore/bytecode/ArrayProfile.cpp +++ b/Source/JavaScriptCore/bytecode/ArrayProfile.cpp @@ -32,6 +32,8 @@ void ArrayProfile::computeUpdatedPrediction(OperationInProgress operation) { if (m_lastSeenStructure) { m_observedArrayModes |= arrayModeFromStructure(m_lastSeenStructure); + m_mayInterceptIndexedAccesses |= + m_lastSeenStructure->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero(); if (!m_structureIsPolymorphic) { if (!m_expectedStructure) m_expectedStructure = m_lastSeenStructure; diff --git a/Source/JavaScriptCore/bytecode/ArrayProfile.h b/Source/JavaScriptCore/bytecode/ArrayProfile.h index 43818d77d..3b462eaba 100644 --- a/Source/JavaScriptCore/bytecode/ArrayProfile.h +++ b/Source/JavaScriptCore/bytecode/ArrayProfile.h @@ -54,6 +54,8 @@ public: , m_lastSeenStructure(0) , m_expectedStructure(0) , m_structureIsPolymorphic(false) + , m_mayStoreToHole(false) + , m_mayInterceptIndexedAccesses(false) , m_observedArrayModes(0) { } @@ -63,6 +65,8 @@ public: , m_lastSeenStructure(0) , m_expectedStructure(0) , m_structureIsPolymorphic(false) + , m_mayStoreToHole(false) + , m_mayInterceptIndexedAccesses(false) , m_observedArrayModes(0) { } @@ -71,6 +75,7 @@ public: Structure** addressOfLastSeenStructure() { return &m_lastSeenStructure; } ArrayModes* addressOfArrayModes() { return &m_observedArrayModes; } + bool* addressOfMayStoreToHole() { return &m_mayStoreToHole; } void observeStructure(Structure* structure) { @@ -89,6 +94,9 @@ public: return !structureIsPolymorphic() && m_expectedStructure; } ArrayModes observedArrayModes() const { return m_observedArrayModes; } + bool mayInterceptIndexedAccesses() const { return m_mayInterceptIndexedAccesses; } + + bool mayStoreToHole() const { return m_mayStoreToHole; } private: friend class LLIntOffsetsExtractor; @@ -97,6 +105,8 @@ private: Structure* m_lastSeenStructure; Structure* m_expectedStructure; bool m_structureIsPolymorphic; + bool m_mayStoreToHole; // This flag may become overloaded to indicate other special cases that were encountered during array access, as it depends on indexing type. Since we currently have basically just one indexing type (two variants of ArrayStorage), this flag for now just means exactly what its name implies. + bool m_mayInterceptIndexedAccesses; ArrayModes m_observedArrayModes; }; diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp index 153ba311c..50b9e2b9f 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp @@ -841,6 +841,7 @@ bool AbstractState::execute(unsigned indexInBlock) node.setCanExit(true); switch (node.arrayMode()) { case Array::Undecided: + case Array::Unprofiled: ASSERT_NOT_REACHED(); break; case Array::ForceExit: @@ -863,6 +864,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(nodeIndex).makeTop(); break; case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: + case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: forNode(node.child2()).filter(SpecInt32); clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).makeTop(); @@ -926,6 +928,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child2).filter(SpecInt32); break; case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: + case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: forNode(child2).filter(SpecInt32); clobberWorld(node.codeOrigin, indexInBlock); break; @@ -1397,6 +1400,20 @@ bool AbstractState::execute(unsigned indexInBlock) } break; } + case Arrayify: { + switch (node.arrayMode()) { + case EFFECTFUL_NON_ARRAY_ARRAY_STORAGE_MODES: + node.setCanExit(true); + forNode(node.child1()).filter(SpecCell); + forNode(nodeIndex).clear(); + clobberStructures(indexInBlock); + break; + default: + ASSERT_NOT_REACHED(); + break; + } + break; + } case GetIndexedPropertyStorage: { switch (node.arrayMode()) { case Array::String: diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp index fe839d54f..ba6673963 100644 --- a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp @@ -41,12 +41,6 @@ namespace JSC { namespace DFG { namespace { -template<typename T> -struct NullableHashTraits : public HashTraits<T> { - static const bool emptyValueIsZero = false; - static T emptyValue() { return reinterpret_cast<T>(1); } -}; - struct ArgumentsAliasingData { InlineCallFrame* callContext; bool callContextSet; @@ -181,7 +175,7 @@ public: VariableAccessData* variableAccessData = node.variableAccessData(); int argumentsRegister = m_graph.uncheckedArgumentsRegisterFor(node.codeOrigin); - if (source.op() != CreateArguments) { + if (source.op() != CreateArguments && source.op() != PhantomArguments) { // Make sure that the source of the SetLocal knows that if it's // a variable that we think is aliased to the arguments, then it // may escape at this point. In future, we could track transitive @@ -435,18 +429,9 @@ public: VariableAccessData* variableAccessData = node.variableAccessData(); if (m_graph.argumentsRegisterFor(node.codeOrigin) == variableAccessData->local() - || unmodifiedArgumentsRegister(m_graph.argumentsRegisterFor(node.codeOrigin)) == variableAccessData->local()) { - // The child of this store should really be the empty value. - Node emptyJSValue(JSConstant, node.codeOrigin, OpInfo(codeBlock()->addOrFindConstant(JSValue()))); - emptyJSValue.ref(); - NodeIndex emptyJSValueIndex = m_graph.size(); - m_graph.deref(node.child1()); - node.children.child1() = Edge(emptyJSValueIndex); - m_graph.append(emptyJSValue); - insertionSet.append(indexInBlock, emptyJSValueIndex); - changed = true; + || unmodifiedArgumentsRegister(m_graph.argumentsRegisterFor(node.codeOrigin)) == variableAccessData->local()) break; - } + ASSERT(!variableAccessData->isCaptured()); // If this is a store into a VariableAccessData* that is marked as @@ -661,25 +646,8 @@ public: insertionSet.execute(*block); } - if (changed) { + if (changed) m_graph.collectGarbage(); - - // Verify that PhantomArguments nodes are not shouldGenerate(). -#if !ASSERT_DISABLED - for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { - BasicBlock* block = m_graph.m_blocks[blockIndex].get(); - if (!block) - continue; - for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { - NodeIndex nodeIndex = block->at(indexInBlock); - Node& node = m_graph[nodeIndex]; - if (node.op() != PhantomArguments) - continue; - ASSERT(!node.shouldGenerate()); - } - } -#endif - } return changed; } diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp index fe2a05b8b..12c9640c8 100644 --- a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp +++ b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp @@ -32,26 +32,39 @@ namespace JSC { namespace DFG { -Array::Mode fromObserved(ArrayModes modes, bool makeSafe) +Array::Mode fromObserved(ArrayProfile* profile, Array::Action action, bool makeSafe) { - // FIXME: we may want to add some polymorphic support in the future. That's why this - // is a switch statement right now. - - switch (modes) { + switch (profile->observedArrayModes()) { case 0: + return Array::Unprofiled; + case asArrayModes(NonArray): + if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) + return Array::BlankToArrayStorage; // FIXME: we don't know whether to go to slow put mode, or not. This is a decent guess. return Array::Undecided; case asArrayModes(NonArrayWithArrayStorage): - return makeSafe ? Array::ArrayStorageOutOfBounds : Array::ArrayStorage; + return makeSafe ? Array::ArrayStorageOutOfBounds : (profile->mayStoreToHole() ? Array::ArrayStorageToHole : Array::ArrayStorage); case asArrayModes(NonArrayWithSlowPutArrayStorage): + case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage): return Array::SlowPutArrayStorage; case asArrayModes(ArrayWithArrayStorage): - return makeSafe ? Array::ArrayWithArrayStorageOutOfBounds : Array::ArrayWithArrayStorage; + return makeSafe ? Array::ArrayWithArrayStorageOutOfBounds : (profile->mayStoreToHole() ? Array::ArrayWithArrayStorageToHole : Array::ArrayWithArrayStorage); case asArrayModes(ArrayWithSlowPutArrayStorage): + case asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage): return Array::ArrayWithSlowPutArrayStorage; case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage): - return makeSafe ? Array::PossiblyArrayWithArrayStorageOutOfBounds : Array::PossiblyArrayWithArrayStorage; + return makeSafe ? Array::PossiblyArrayWithArrayStorageOutOfBounds : (profile->mayStoreToHole() ? Array::PossiblyArrayWithArrayStorageToHole : Array::PossiblyArrayWithArrayStorage); case asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage): + case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage): return Array::PossiblyArrayWithSlowPutArrayStorage; + case asArrayModes(NonArray) | asArrayModes(NonArrayWithArrayStorage): + if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) + return Array::BlankToArrayStorage; + return Array::Undecided; + case asArrayModes(NonArray) | asArrayModes(NonArrayWithSlowPutArrayStorage): + case asArrayModes(NonArray) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage): + if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) + return Array::BlankToSlowPutArrayStorage; + return Array::Undecided; 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 @@ -61,11 +74,6 @@ Array::Mode fromObserved(ArrayModes modes, bool makeSafe) } } -Array::Mode fromStructure(Structure* structure, bool makeSafe) -{ - return fromObserved(arrayModeFromStructure(structure), makeSafe); -} - Array::Mode refineArrayMode(Array::Mode arrayMode, SpeculatedType base, SpeculatedType index) { if (!base || !index) { @@ -79,6 +87,12 @@ Array::Mode refineArrayMode(Array::Mode arrayMode, SpeculatedType base, Speculat if (!isInt32Speculation(index) || !isCellSpeculation(base)) return Array::Generic; + if (arrayMode == 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; + } + if (arrayMode != Array::Undecided) return arrayMode; @@ -131,8 +145,10 @@ bool modeAlreadyChecked(AbstractValue& value, Array::Mode arrayMode) return isStringSpeculation(value.m_type); case Array::ArrayStorage: + case Array::ArrayStorageToHole: case Array::ArrayStorageOutOfBounds: case Array::PossiblyArrayWithArrayStorage: + case Array::PossiblyArrayWithArrayStorageToHole: case Array::PossiblyArrayWithArrayStorageOutOfBounds: return value.m_currentKnownStructure.hasSingleton() && (value.m_currentKnownStructure.singleton()->indexingType() & HasArrayStorage); @@ -140,9 +156,10 @@ bool modeAlreadyChecked(AbstractValue& value, Array::Mode arrayMode) case Array::SlowPutArrayStorage: case Array::PossiblyArrayWithSlowPutArrayStorage: return value.m_currentKnownStructure.hasSingleton() - && (value.m_currentKnownStructure.singleton()->indexingType() & HasSlowPutArrayStorage); + && (value.m_currentKnownStructure.singleton()->indexingType() & (HasArrayStorage | HasSlowPutArrayStorage)); case Array::ArrayWithArrayStorage: + case Array::ArrayWithArrayStorageToHole: case Array::ArrayWithArrayStorageOutOfBounds: return value.m_currentKnownStructure.hasSingleton() && (value.m_currentKnownStructure.singleton()->indexingType() & HasArrayStorage) @@ -150,9 +167,12 @@ bool modeAlreadyChecked(AbstractValue& value, Array::Mode arrayMode) case Array::ArrayWithSlowPutArrayStorage: return value.m_currentKnownStructure.hasSingleton() - && (value.m_currentKnownStructure.singleton()->indexingType() & HasSlowPutArrayStorage) + && (value.m_currentKnownStructure.singleton()->indexingType() & (HasArrayStorage | HasSlowPutArrayStorage)) && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); + case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: + return false; + case Array::Arguments: return isArgumentsSpeculation(value.m_type); @@ -184,6 +204,7 @@ bool modeAlreadyChecked(AbstractValue& value, Array::Mode arrayMode) return isFloat64ArraySpeculation(value.m_type); case Array::Undecided: + case Array::Unprofiled: break; } @@ -196,6 +217,8 @@ const char* modeToString(Array::Mode mode) switch (mode) { case Array::Undecided: return "Undecided"; + case Array::Unprofiled: + return "Unprofiled"; case Array::Generic: return "Generic"; case Array::ForceExit: @@ -204,22 +227,32 @@ const char* modeToString(Array::Mode mode) return "String"; 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::BlankToArrayStorage: + return "BlankToArrayStorage"; + case Array::BlankToSlowPutArrayStorage: + return "BlankToSlowPutArrayStorage"; case Array::Arguments: return "Arguments"; case Array::Int8Array: diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.h b/Source/JavaScriptCore/dfg/DFGArrayMode.h index cc8b1b809..d4be9c0eb 100644 --- a/Source/JavaScriptCore/dfg/DFGArrayMode.h +++ b/Source/JavaScriptCore/dfg/DFGArrayMode.h @@ -41,20 +41,36 @@ struct AbstractValue; // that would otherwise occur, since we say things like "Int8Array" and "JSArray" // in lots of other places, to mean subtly different things. namespace Array { +enum Action { + Read, + Write +}; + enum Mode { Undecided, // 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. ArrayStorage, + ArrayStorageToHole, SlowPutArrayStorage, ArrayStorageOutOfBounds, ArrayWithArrayStorage, + ArrayWithArrayStorageToHole, ArrayWithSlowPutArrayStorage, ArrayWithArrayStorageOutOfBounds, PossiblyArrayWithArrayStorage, + PossiblyArrayWithArrayStorageToHole, PossiblyArrayWithSlowPutArrayStorage, PossiblyArrayWithArrayStorageOutOfBounds, + + // Modes of conventional indexed storage where the check is side-effecting. + BlankToArrayStorage, + BlankToSlowPutArrayStorage, + Arguments, Int8Array, Int16Array, @@ -71,22 +87,32 @@ enum Mode { // 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_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 ARRAY_STORAGE_TO_HOLE_MODES \ + Array::ArrayStorageToHole: \ + case Array::ArrayWithArrayStorageToHole: \ + case Array::PossiblyArrayWithArrayStorageToHole #define IN_BOUNDS_ARRAY_STORAGE_MODES \ - Array::ArrayStorage: \ + ARRAY_STORAGE_TO_HOLE_MODES: \ + case Array::ArrayStorage: \ case Array::ArrayWithArrayStorage: \ case Array::PossiblyArrayWithArrayStorage #define SLOW_PUT_ARRAY_STORAGE_MODES \ @@ -99,9 +125,16 @@ enum Mode { case Array::ArrayWithArrayStorageOutOfBounds: \ case Array::PossiblyArrayWithArrayStorageOutOfBounds -Array::Mode fromObserved(ArrayModes modes, bool makeSafe); +// Next: helpers for side-effecting checks. +#define EFFECTFUL_NON_ARRAY_ARRAY_STORAGE_MODES \ + Array::BlankToArrayStorage: \ + case Array::BlankToSlowPutArrayStorage +#define ALL_EFFECTFUL_ARRAY_STORAGE_MODES \ + EFFECTFUL_NON_ARRAY_ARRAY_STORAGE_MODES +#define SLOW_PUT_EFFECTFUL_ARRAY_STORAGE_MODES \ + Array::BlankToSlowPutArrayStorage -Array::Mode fromStructure(Structure*, bool makeSafe); +Array::Mode fromObserved(ArrayProfile*, Array::Action, bool makeSafe); Array::Mode refineArrayMode(Array::Mode, SpeculatedType base, SpeculatedType index); @@ -113,6 +146,7 @@ inline bool modeUsesButterfly(Array::Mode arrayMode) { switch (arrayMode) { case ALL_ARRAY_STORAGE_MODES: + case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: return true; default: return false; @@ -143,6 +177,19 @@ 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; + } +} + +inline bool mayStoreToHole(Array::Mode arrayMode) +{ + switch (arrayMode) { + case ARRAY_STORAGE_TO_HOLE_MODES: + case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: + case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: return true; default: return false; @@ -153,6 +200,7 @@ inline bool canCSEStorage(Array::Mode arrayMode) { switch (arrayMode) { case Array::Undecided: + case Array::Unprofiled: case Array::ForceExit: case Array::Generic: case Array::Arguments: @@ -185,6 +233,7 @@ inline bool modeIsSpecific(Array::Mode mode) { switch (mode) { case Array::Undecided: + case Array::Unprofiled: case Array::ForceExit: case Array::Generic: return false; @@ -197,6 +246,7 @@ inline bool modeSupportsLength(Array::Mode mode) { switch (mode) { case Array::Undecided: + case Array::Unprofiled: case Array::ForceExit: case Array::Generic: case NON_ARRAY_ARRAY_STORAGE_MODES: @@ -206,6 +256,30 @@ inline bool modeSupportsLength(Array::Mode mode) } } +inline bool benefitsFromStructureCheck(Array::Mode mode) +{ + switch (mode) { + case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: + case Array::Undecided: + case Array::Unprofiled: + case Array::ForceExit: + case Array::Generic: + return false; + default: + return true; + } +} + +inline bool isEffectful(Array::Mode mode) +{ + switch (mode) { + case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: + return true; + default: + return false; + } +} + } } // namespace JSC::DFG #endif // ENABLE(DFG_JIT) diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index f8ef37b03..fb897ff5b 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -828,26 +828,29 @@ private: Array::Mode getArrayMode(ArrayProfile* profile) { profile->computeUpdatedPrediction(); - return fromObserved(profile->observedArrayModes(), false); + return fromObserved(profile, Array::Read, false); } - Array::Mode getArrayModeAndEmitChecks(ArrayProfile* profile, NodeIndex base) + Array::Mode getArrayModeAndEmitChecks(ArrayProfile* profile, Array::Action action, NodeIndex base) { profile->computeUpdatedPrediction(); - if (profile->hasDefiniteStructure()) - addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(profile->expectedStructure())), base); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) if (m_inlineStackTop->m_profiledBlock->numberOfRareCaseProfiles()) dataLog("Slow case profile for bc#%u: %u\n", m_currentIndex, m_inlineStackTop->m_profiledBlock->rareCaseProfileForBytecodeOffset(m_currentIndex)->m_counter); - dataLog("Array profile for bc#%u: %p%s, %u\n", m_currentIndex, profile->expectedStructure(), profile->structureIsPolymorphic() ? " (polymorphic)" : "", profile->observedArrayModes()); + dataLog("Array profile for bc#%u: %p%s%s, %u\n", m_currentIndex, profile->expectedStructure(), profile->structureIsPolymorphic() ? " (polymorphic)" : "", profile->mayInterceptIndexedAccesses() ? " (may intercept)" : "", profile->observedArrayModes()); #endif bool makeSafe = m_inlineStackTop->m_profiledBlock->couldTakeSlowCase(m_currentIndex) || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, OutOfBounds); - return fromObserved(profile->observedArrayModes(), makeSafe); + Array::Mode result = fromObserved(profile, action, makeSafe); + + if (profile->hasDefiniteStructure() && benefitsFromStructureCheck(result)) + addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(profile->expectedStructure())), base); + + return result; } NodeIndex makeSafe(NodeIndex nodeIndex) @@ -2188,7 +2191,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) SpeculatedType prediction = getPrediction(); NodeIndex base = get(currentInstruction[2].u.operand); - Array::Mode arrayMode = getArrayModeAndEmitChecks(currentInstruction[4].u.arrayProfile, base); + Array::Mode 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); set(currentInstruction[1].u.operand, getByVal); @@ -2199,7 +2202,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, base); + Array::Mode arrayMode = getArrayModeAndEmitChecks(currentInstruction[4].u.arrayProfile, Array::Write, base); NodeIndex property = get(currentInstruction[2].u.operand); NodeIndex value = get(currentInstruction[3].u.operand); diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp index 0914f62c6..111c15f17 100644 --- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp @@ -592,6 +592,7 @@ 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) diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index 2f92e5608..aa2d5dff4 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -90,7 +90,7 @@ private: if (arrayProfile) { arrayProfile->computeUpdatedPrediction(); arrayMode = refineArrayMode( - fromObserved(arrayProfile->observedArrayModes(), false), + fromObserved(arrayProfile, Array::Read, false), m_graph[node.child1()].prediction(), m_graph[m_compileIndex].prediction()); if (modeSupportsLength(arrayMode) && arrayProfile->hasDefiniteStructure()) { @@ -380,6 +380,23 @@ private: ASSERT(modeIsSpecific(arrayMode)); m_graph.ref(array); + + if (isEffectful(arrayMode)) { + Node arrayify(Arrayify, codeOrigin, OpInfo(arrayMode), array); + 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(storageCheck == canCSEStorage); + ASSERT(shouldGenerate); + ASSERT(canCSEStorage(arrayMode)); + ASSERT(modeUsesButterfly(arrayMode)); + + return arrayifyIndex; + } + Node checkArray(CheckArray, codeOrigin, OpInfo(arrayMode), array); checkArray.ref(); NodeIndex checkArrayIndex = m_graph.size(); diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h index 4166f72c9..64d81f526 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.h +++ b/Source/JavaScriptCore/dfg/DFGGraph.h @@ -474,6 +474,7 @@ public: switch (node.arrayMode()) { case Array::Generic: case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: + case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: return false; case Array::String: return node.op() == GetByVal; diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h index a4358798a..195135c7b 100644 --- a/Source/JavaScriptCore/dfg/DFGNode.h +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -740,6 +740,7 @@ struct Node { case StringCharAt: case StringCharCodeAt: case CheckArray: + case Arrayify: case ArrayPush: case ArrayPop: return true; @@ -806,6 +807,7 @@ struct Node { case ValueToInt32: case UInt32ToNumber: case DoubleAsInt32: + case PhantomArguments: return true; case Phantom: case Nop: diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h index 4c2a85ccc..584e28cca 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeType.h +++ b/Source/JavaScriptCore/dfg/DFGNodeType.h @@ -139,6 +139,7 @@ namespace JSC { namespace DFG { macro(ReallocatePropertyStorage, NodeMustGenerate | NodeDoesNotExit | NodeResultStorage) \ macro(GetButterfly, NodeResultStorage) \ macro(CheckArray, NodeMustGenerate) \ + macro(Arrayify, NodeResultStorage | NodeMustGenerate | NodeClobbersWorld) \ macro(GetIndexedPropertyStorage, NodeResultStorage) \ macro(GetByOffset, NodeResultJS) \ macro(PutByOffset, NodeMustGenerate) \ diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp index 6bc136da4..8c8e2f949 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp @@ -612,6 +612,9 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov // registers. if (haveArguments) { + HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash, + NullableHashTraits<InlineCallFrame*> > didCreateArgumentsObject; + for (size_t index = 0; index < operands.size(); ++index) { const ValueRecovery& recovery = operands[index]; if (recovery.technique() != ArgumentsThatWereNotCreated) @@ -627,44 +630,42 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov break; } } + int argumentsRegister = m_jit.argumentsRegisterFor(inlineCallFrame); - - m_jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0); - AssemblyHelpers::Jump haveArguments = m_jit.branch32( - AssemblyHelpers::NotEqual, - AssemblyHelpers::tagFor(argumentsRegister), - AssemblyHelpers::TrustedImm32(JSValue::EmptyValueTag)); - - if (inlineCallFrame) { - m_jit.setupArgumentsWithExecState( - AssemblyHelpers::TrustedImmPtr(inlineCallFrame)); - m_jit.move( - AssemblyHelpers::TrustedImmPtr( - bitwise_cast<void*>(operationCreateInlinedArguments)), - GPRInfo::nonArgGPR0); - } else { - m_jit.setupArgumentsExecState(); - m_jit.move( - AssemblyHelpers::TrustedImmPtr( - bitwise_cast<void*>(operationCreateArguments)), - GPRInfo::nonArgGPR0); + if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) { + // We know this call frame optimized out an arguments object that + // the baseline JIT would have created. Do that creation now. + if (inlineCallFrame) { + m_jit.setupArgumentsWithExecState( + AssemblyHelpers::TrustedImmPtr(inlineCallFrame)); + m_jit.move( + AssemblyHelpers::TrustedImmPtr( + bitwise_cast<void*>(operationCreateInlinedArguments)), + GPRInfo::nonArgGPR0); + } else { + m_jit.setupArgumentsExecState(); + m_jit.move( + AssemblyHelpers::TrustedImmPtr( + bitwise_cast<void*>(operationCreateArguments)), + GPRInfo::nonArgGPR0); + } + m_jit.call(GPRInfo::nonArgGPR0); + m_jit.store32( + AssemblyHelpers::TrustedImm32(JSValue::CellTag), + AssemblyHelpers::tagFor(argumentsRegister)); + m_jit.store32( + GPRInfo::returnValueGPR, + AssemblyHelpers::payloadFor(argumentsRegister)); + m_jit.store32( + AssemblyHelpers::TrustedImm32(JSValue::CellTag), + AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister))); + m_jit.store32( + GPRInfo::returnValueGPR, + AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister))); + m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms. } - m_jit.call(GPRInfo::nonArgGPR0); - m_jit.store32( - AssemblyHelpers::TrustedImm32(JSValue::CellTag), - AssemblyHelpers::tagFor(argumentsRegister)); - m_jit.store32( - GPRInfo::returnValueGPR, - AssemblyHelpers::payloadFor(argumentsRegister)); - m_jit.store32( - AssemblyHelpers::TrustedImm32(JSValue::CellTag), - AssemblyHelpers::tagFor(unmodifiedArgumentsRegister(argumentsRegister))); - m_jit.store32( - GPRInfo::returnValueGPR, - AssemblyHelpers::payloadFor(unmodifiedArgumentsRegister(argumentsRegister))); - m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms. - - haveArguments.link(&m_jit); + + m_jit.load32(AssemblyHelpers::payloadFor(argumentsRegister), GPRInfo::regT0); m_jit.store32( AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor(operand)); diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp index 2f38ba79b..fcaf0a4bc 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp @@ -587,6 +587,9 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov // registers. if (haveArguments) { + HashSet<InlineCallFrame*, DefaultHash<InlineCallFrame*>::Hash, + NullableHashTraits<InlineCallFrame*> > didCreateArgumentsObject; + for (size_t index = 0; index < operands.size(); ++index) { const ValueRecovery& recovery = operands[index]; if (recovery.technique() != ArgumentsThatWereNotCreated) @@ -602,29 +605,29 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov break; } } + int argumentsRegister = m_jit.argumentsRegisterFor(inlineCallFrame); - + if (didCreateArgumentsObject.add(inlineCallFrame).isNewEntry) { + // We know this call frame optimized out an arguments object that + // the baseline JIT would have created. Do that creation now. + if (inlineCallFrame) { + m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0); + m_jit.setupArguments(GPRInfo::regT0); + } else + m_jit.setupArgumentsExecState(); + m_jit.move( + AssemblyHelpers::TrustedImmPtr( + bitwise_cast<void*>(operationCreateArguments)), + GPRInfo::nonArgGPR0); + m_jit.call(GPRInfo::nonArgGPR0); + m_jit.storePtr(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister)); + m_jit.storePtr( + GPRInfo::returnValueGPR, + AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister))); + m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms. + } + m_jit.loadPtr(AssemblyHelpers::addressFor(argumentsRegister), GPRInfo::regT0); - AssemblyHelpers::Jump haveArguments = m_jit.branchTestPtr( - AssemblyHelpers::NonZero, GPRInfo::regT0); - - if (inlineCallFrame) { - m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT0); - m_jit.setupArguments(GPRInfo::regT0); - } else - m_jit.setupArgumentsExecState(); - m_jit.move( - AssemblyHelpers::TrustedImmPtr( - bitwise_cast<void*>(operationCreateArguments)), - GPRInfo::nonArgGPR0); - m_jit.call(GPRInfo::nonArgGPR0); - m_jit.storePtr(GPRInfo::returnValueGPR, AssemblyHelpers::addressFor(argumentsRegister)); - m_jit.storePtr( - GPRInfo::returnValueGPR, - AssemblyHelpers::addressFor(unmodifiedArgumentsRegister(argumentsRegister))); - m_jit.move(GPRInfo::returnValueGPR, GPRInfo::regT0); // no-op move on almost all platforms. - - haveArguments.link(&m_jit); m_jit.storePtr(GPRInfo::regT0, AssemblyHelpers::addressFor(operand)); } } diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index 67ae7bf03..3452b2f0d 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -1193,9 +1193,11 @@ void DFG_OPERATION operationTearOffInlinedArguments( EncodedJSValue DFG_OPERATION operationGetArgumentsLength(ExecState* exec, int32_t argumentsRegister) { + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); // Here we can assume that the argumernts were created. Because otherwise the JIT code would // have not made this call. - Identifier ident(&exec->globalData(), "length"); + Identifier ident(&globalData, "length"); JSValue baseValue = exec->uncheckedR(argumentsRegister).jsValue(); PropertySlot slot(baseValue); return JSValue::encode(baseValue.get(exec, ident, slot)); @@ -1203,6 +1205,9 @@ EncodedJSValue DFG_OPERATION operationGetArgumentsLength(ExecState* exec, int32_ EncodedJSValue DFG_OPERATION operationGetArgumentByVal(ExecState* exec, int32_t argumentsRegister, int32_t index) { + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); + JSValue argumentsValue = exec->uncheckedR(argumentsRegister).jsValue(); // If there are no arguments, and we're accessing out of bounds, then we have to create the @@ -1216,6 +1221,9 @@ EncodedJSValue DFG_OPERATION operationGetArgumentByVal(ExecState* exec, int32_t EncodedJSValue DFG_OPERATION operationGetInlinedArgumentByVal( ExecState* exec, int32_t argumentsRegister, InlineCallFrame* inlineCallFrame, int32_t index) { + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); + JSValue argumentsValue = exec->uncheckedR(argumentsRegister).jsValue(); // If there are no arguments, and we're accessing out of bounds, then we have to create the @@ -1239,6 +1247,10 @@ JSCell* DFG_OPERATION operationNewFunction(ExecState* exec, JSCell* functionExec JSCell* DFG_OPERATION operationNewFunctionExpression(ExecState* exec, JSCell* functionExecutableAsCell) { ASSERT(functionExecutableAsCell->inherits(&FunctionExecutable::s_info)); + + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); + FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(functionExecutableAsCell); return JSFunction::create(exec, functionExecutable, exec->scope()); @@ -1257,6 +1269,8 @@ size_t DFG_OPERATION operationIsFunction(EncodedJSValue value) void DFG_OPERATION operationReallocateStorageAndFinishPut(ExecState* exec, JSObject* base, Structure* structure, PropertyOffset offset, EncodedJSValue value) { JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); + ASSERT(structure->outOfLineCapacity() > base->structure()->outOfLineCapacity()); ASSERT(!globalData.heap.storageAllocator().fastPathShouldSucceed(structure->outOfLineCapacity() * sizeof(JSValue))); base->setStructureAndReallocateStorageIfNecessary(globalData, structure); @@ -1265,31 +1279,51 @@ void DFG_OPERATION operationReallocateStorageAndFinishPut(ExecState* exec, JSObj char* DFG_OPERATION operationAllocatePropertyStorageWithInitialCapacity(ExecState* exec) { + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); + return reinterpret_cast<char*>( - Butterfly::createUninitialized(exec->globalData(), 0, initialOutOfLineCapacity, false, 0)); + Butterfly::createUninitialized(globalData, 0, initialOutOfLineCapacity, false, 0)); } char* DFG_OPERATION operationAllocatePropertyStorage(ExecState* exec, size_t newSize) { + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); + return reinterpret_cast<char*>( - Butterfly::createUninitialized(exec->globalData(), 0, newSize, false, 0)); + Butterfly::createUninitialized(globalData, 0, newSize, false, 0)); } char* DFG_OPERATION operationReallocateButterflyToHavePropertyStorageWithInitialCapacity(ExecState* exec, JSObject* object) { + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); + ASSERT(!object->structure()->outOfLineCapacity()); - Butterfly* result = object->growOutOfLineStorage(exec->globalData(), 0, initialOutOfLineCapacity); + Butterfly* result = object->growOutOfLineStorage(globalData, 0, initialOutOfLineCapacity); object->setButterflyWithoutChangingStructure(result); return reinterpret_cast<char*>(result); } char* DFG_OPERATION operationReallocateButterflyToGrowPropertyStorage(ExecState* exec, JSObject* object, size_t newSize) { - Butterfly* result = object->growOutOfLineStorage(exec->globalData(), object->structure()->outOfLineCapacity(), newSize); + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); + + Butterfly* result = object->growOutOfLineStorage(globalData, object->structure()->outOfLineCapacity(), newSize); object->setButterflyWithoutChangingStructure(result); return reinterpret_cast<char*>(result); } +char* DFG_OPERATION operationEnsureArrayStorage(ExecState* exec, JSObject* object) +{ + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); + + return reinterpret_cast<char*>(object->ensureArrayStorage(globalData)); +} + double DFG_OPERATION operationFModOnInts(int32_t a, int32_t b) { return fmod(a, b); diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h index 02c6b3ab3..f86f5cf1f 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.h +++ b/Source/JavaScriptCore/dfg/DFGOperations.h @@ -193,6 +193,7 @@ char* DFG_OPERATION operationAllocatePropertyStorageWithInitialCapacity(ExecStat char* DFG_OPERATION operationAllocatePropertyStorage(ExecState*, size_t newSize) WTF_INTERNAL; char* DFG_OPERATION operationReallocateButterflyToHavePropertyStorageWithInitialCapacity(ExecState*, JSObject*) WTF_INTERNAL; char* DFG_OPERATION operationReallocateButterflyToGrowPropertyStorage(ExecState*, JSObject*, size_t newSize) WTF_INTERNAL; +char* DFG_OPERATION operationEnsureArrayStorage(ExecState*, JSObject*); // This method is used to lookup an exception hander, keyed by faultLocation, which is // the return location from one of the calls out to one of the helper operations above. diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index 5b23650fa..a918bbbe5 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -619,7 +619,8 @@ private: case GetMyArgumentByVal: case PhantomPutStructure: case PhantomArguments: - case CheckArray: { + case CheckArray: + case Arrayify: { // 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/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 07cb11032..0228f846a 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -325,7 +325,7 @@ void SpeculativeJIT::checkArray(Node& node) MacroAssembler::Zero, MacroAssembler::Address(temp.gpr(), Structure::indexingTypeOffset()), MacroAssembler::TrustedImm32( - isSlowPutAccess(node.arrayMode()) ? HasSlowPutArrayStorage : HasArrayStorage))); + isSlowPutAccess(node.arrayMode()) ? (HasArrayStorage | HasSlowPutArrayStorage) : HasArrayStorage))); noResult(m_compileIndex); return; @@ -347,7 +347,7 @@ void SpeculativeJIT::checkArray(Node& node) Uncountable, JSValueRegs(), NoNode, m_jit.branchTest32( MacroAssembler::Zero, tempGPR, MacroAssembler::TrustedImm32( - isSlowPutAccess(node.arrayMode()) ? HasSlowPutArrayStorage : HasArrayStorage))); + isSlowPutAccess(node.arrayMode()) ? (HasArrayStorage | HasSlowPutArrayStorage) : HasArrayStorage))); noResult(m_compileIndex); return; @@ -384,6 +384,80 @@ void SpeculativeJIT::checkArray(Node& node) noResult(m_compileIndex); } +void SpeculativeJIT::arrayify(Node& node) +{ + ASSERT(modeIsSpecific(node.arrayMode())); + ASSERT(!modeAlreadyChecked(m_state.forNode(node.child1()), node.arrayMode())); + + SpeculateCellOperand base(this, node.child1()); + GPRReg baseReg = base.gpr(); + + switch (node.arrayMode()) { + case EFFECTFUL_NON_ARRAY_ARRAY_STORAGE_MODES: { + GPRTemporary structure(this); + GPRTemporary temp(this); + GPRReg structureGPR = structure.gpr(); + GPRReg tempGPR = temp.gpr(); + + m_jit.loadPtr( + MacroAssembler::Address(baseReg, JSCell::structureOffset()), structureGPR); + + // We can skip all that comes next if we already have array storage. + IndexingType desiredIndexingTypeMask = + isSlowPutAccess(node.arrayMode()) ? (HasArrayStorage | HasSlowPutArrayStorage) : HasArrayStorage; + MacroAssembler::Jump slowCase = m_jit.branchTest8( + MacroAssembler::Zero, + MacroAssembler::Address(structureGPR, Structure::indexingTypeOffset()), + MacroAssembler::TrustedImm32(desiredIndexingTypeMask)); + + m_jit.loadPtr( + MacroAssembler::Address(baseReg, JSObject::butterflyOffset()), tempGPR); + + MacroAssembler::Jump done = m_jit.jump(); + + slowCase.link(&m_jit); + + // Next check that the object does not intercept indexed accesses. If it does, + // then this mode won't work. + speculationCheck( + Uncountable, JSValueRegs(), NoNode, + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero))); + + // Now call out to create the array storage. + silentSpillAllRegisters(tempGPR); + callOperation(operationEnsureArrayStorage, tempGPR, baseReg); + 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. + speculationCheck( + Uncountable, JSValueRegs(), NoNode, + m_jit.branchTest8( + MacroAssembler::Zero, + MacroAssembler::Address(structureGPR, Structure::indexingTypeOffset()), + MacroAssembler::TrustedImm32(desiredIndexingTypeMask))); + + done.link(&m_jit); + storageResult(tempGPR, m_compileIndex); + break; + } + default: + ASSERT_NOT_REACHED(); + break; + } +} + GPRReg SpeculativeJIT::fillStorage(NodeIndex nodeIndex) { Node& node = m_jit.graph()[nodeIndex]; diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 286b72c1a..15314b2f2 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -2265,6 +2265,7 @@ public: const TypedArrayDescriptor* typedArrayDescriptor(Array::Mode); void checkArray(Node&); + void arrayify(Node&); template<bool strict> GPRReg fillSpeculateIntInternal(NodeIndex, DataFormat& returnFormat); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index 85e32ddb9..22941358a 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -2055,9 +2055,6 @@ void SpeculativeJIT::compile(Node& node) break; case PhantomArguments: - // This should never be must-generate. - ASSERT_NOT_REACHED(); - // But as a release-mode fall-back make it the empty value. initConstantInfo(m_compileIndex); break; @@ -2244,6 +2241,15 @@ void SpeculativeJIT::compile(Node& node) m_jit.store32(value.tagGPR(), JITCompiler::tagFor(node.local())); noResult(m_compileIndex); recordSetLocal(node.local(), ValueSource(ValueInRegisterFile)); + + // If we're storing an arguments object that has been optimized away, + // our variable event stream for OSR exit now reflects the optimized + // value (JSValue()). On the slow path, we want an arguments object + // instead. We add an additional move hint to show OSR exit that it + // needs to reconstruct the arguments object. + if (at(node.child1()).op() == PhantomArguments) + compileMovHint(node); + break; } @@ -2531,6 +2537,11 @@ void SpeculativeJIT::compile(Node& node) checkArray(node); break; } + + case Arrayify: { + arrayify(node); + break; + } case GetByVal: { switch (node.arrayMode()) { @@ -2575,7 +2586,8 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); break; } - case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: { + case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: + case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: { SpeculateCellOperand base(this, node.child1()); SpeculateStrictInt32Operand property(this, node.child2()); StorageOperand storage(this, node.child3()); @@ -2702,7 +2714,8 @@ void SpeculativeJIT::compile(Node& node) GPRReg propertyReg = property.gpr(); switch (arrayMode) { - case ALL_ARRAY_STORAGE_MODES: { + case ALL_ARRAY_STORAGE_MODES: + case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: { JSValueOperand value(this, child3); GPRReg valueTagReg = value.tagGPR(); @@ -2734,31 +2747,40 @@ void SpeculativeJIT::compile(Node& node) if (isInBoundsAccess(node.arrayMode())) speculationCheck(OutOfBounds, JSValueRegs(), NoNode, beyondArrayBounds); - base.use(); - property.use(); - value.use(); - storage.use(); - // Check if we're writing to a hole; if so increment m_numValuesInVector. - 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)); MacroAssembler::Jump isHoleValue; - 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. - isHoleValue = m_jit.jump(); + 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 { - m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); - - // 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); + 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. + isHoleValue = m_jit.jump(); + } else { + m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); + + // 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); } - notHoleValue.link(&m_jit); + + base.use(); + property.use(); + value.use(); + storage.use(); // 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))); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index f050af699..87be658ad 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -2119,9 +2119,6 @@ void SpeculativeJIT::compile(Node& node) break; case PhantomArguments: - // This should never be must-generate. - ASSERT_NOT_REACHED(); - // But as a release-mode fall-back make it the empty value. initConstantInfo(m_compileIndex); break; @@ -2280,6 +2277,15 @@ void SpeculativeJIT::compile(Node& node) noResult(m_compileIndex); recordSetLocal(node.local(), ValueSource(ValueInRegisterFile)); + + // If we're storing an arguments object that has been optimized away, + // our variable event stream for OSR exit now reflects the optimized + // value (JSValue()). On the slow path, we want an arguments object + // instead. We add an additional move hint to show OSR exit that it + // needs to reconstruct the arguments object. + if (at(node.child1()).op() == PhantomArguments) + compileMovHint(node); + break; } @@ -2563,6 +2569,11 @@ void SpeculativeJIT::compile(Node& node) checkArray(node); break; } + + case Arrayify: { + arrayify(node); + break; + } case GetByVal: { switch (node.arrayMode()) { @@ -2603,7 +2614,8 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(result.gpr(), m_compileIndex); break; } - case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: { + case OUT_OF_BOUNDS_ARRAY_STORAGE_MODES: + case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: { SpeculateCellOperand base(this, node.child1()); SpeculateStrictInt32Operand property(this, node.child2()); StorageOperand storage(this, node.child3()); @@ -2723,7 +2735,8 @@ void SpeculativeJIT::compile(Node& node) GPRReg propertyReg = property.gpr(); switch (arrayMode) { - case ALL_ARRAY_STORAGE_MODES: { + case ALL_ARRAY_STORAGE_MODES: + case ALL_EFFECTFUL_ARRAY_STORAGE_MODES: { JSValueOperand value(this, child3); GPRReg valueReg = value.gpr(); @@ -2753,31 +2766,40 @@ void SpeculativeJIT::compile(Node& node) if (isInBoundsAccess(arrayMode)) speculationCheck(OutOfBounds, JSValueRegs(), NoNode, beyondArrayBounds); - base.use(); - property.use(); - value.use(); - storage.use(); - // Check if we're writing to a hole; if so increment m_numValuesInVector. - MacroAssembler::Jump notHoleValue = m_jit.branchTestPtr(MacroAssembler::NonZero, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); MacroAssembler::Jump isHoleValue; - 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. - isHoleValue = m_jit.jump(); + 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.branchTestPtr(MacroAssembler::Zero, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])))); } else { - m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); + MacroAssembler::Jump notHoleValue = m_jit.branchTestPtr(MacroAssembler::NonZero, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, 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. + isHoleValue = m_jit.jump(); + } else { + m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); - // 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); + // 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); + lengthDoesNotNeedUpdate.link(&m_jit); + } + notHoleValue.link(&m_jit); } - notHoleValue.link(&m_jit); + + base.use(); + property.use(); + value.use(); + storage.use(); // Store the value to the array. m_jit.storePtr(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h index ac7c8765b..150aae9ea 100644 --- a/Source/JavaScriptCore/jit/JIT.h +++ b/Source/JavaScriptCore/jit/JIT.h @@ -451,6 +451,7 @@ namespace JSC { #endif void emitArrayProfilingSite(RegisterID structureAndIndexingType, RegisterID scratch, ArrayProfile*); void emitArrayProfilingSiteForBytecodeIndex(RegisterID structureAndIndexingType, RegisterID scratch, unsigned bytecodeIndex); + void emitArrayProfileStoreToHoleSpecialCase(ArrayProfile*); enum FinalObjectMode { MayBeFinal, KnownNotFinal }; diff --git a/Source/JavaScriptCore/jit/JITInlineMethods.h b/Source/JavaScriptCore/jit/JITInlineMethods.h index 302e109ca..a4f9107df 100644 --- a/Source/JavaScriptCore/jit/JITInlineMethods.h +++ b/Source/JavaScriptCore/jit/JITInlineMethods.h @@ -556,6 +556,14 @@ inline void JIT::emitArrayProfilingSiteForBytecodeIndex(RegisterID structureAndI #endif } +inline void JIT::emitArrayProfileStoreToHoleSpecialCase(ArrayProfile* arrayProfile) +{ + if (!canBeOptimized()) + return; + + store8(TrustedImm32(1), arrayProfile->addressOfMayStoreToHole()); +} + #if USE(JSVALUE32_64) inline void JIT::emitLoadTag(int index, RegisterID tag) diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp index bbc289838..b4d52e225 100644 --- a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp +++ b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp @@ -247,6 +247,7 @@ void JIT::emit_op_put_by_val(Instruction* currentInstruction) Jump end = jump(); empty.link(this); + emitArrayProfileStoreToHoleSpecialCase(currentInstruction[4].u.arrayProfile); add32(TrustedImm32(1), Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); branch32(Below, regT1, Address(regT2, ArrayStorage::lengthOffset())).linkTo(storeResult, this); diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp index 1692f33c3..ed561a28b 100644 --- a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp +++ b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp @@ -283,6 +283,7 @@ void JIT::emit_op_put_by_val(Instruction* currentInstruction) Jump end = jump(); empty.link(this); + emitArrayProfileStoreToHoleSpecialCase(currentInstruction[4].u.arrayProfile); add32(TrustedImm32(1), Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); branch32(Below, regT2, Address(regT3, ArrayStorage::lengthOffset())).linkTo(storeResult, this); diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm index 41926d8db..953bb3a92 100644 --- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm +++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm @@ -1388,8 +1388,8 @@ _llint_op_put_by_val: loadi 4[PC], t0 loadConstantOrVariablePayload(t0, CellTag, t1, .opPutByValSlow) loadp JSCell::m_structure[t1], t2 - loadp 16[PC], t0 - arrayProfile(t2, t0, t3) + loadp 16[PC], t3 + arrayProfile(t2, t3, t0) btiz t2, HasArrayStorage, .opPutByValSlow loadi 8[PC], t0 loadConstantOrVariablePayload(t0, Int32Tag, t2, .opPutByValSlow) @@ -1405,6 +1405,7 @@ _llint_op_put_by_val: dispatch(5) .opPutByValEmpty: + storeb 1, ArrayProfile::m_mayStoreToHole[t3] addi 1, ArrayStorage::m_numValuesInVector[t0] bib t2, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t0], .opPutByValStoreResult addi 1, t2, t1 diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm index 51a11f53f..812be0ec9 100644 --- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm +++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm @@ -1231,8 +1231,8 @@ _llint_op_put_by_val: loadis 8[PB, PC, 8], t0 loadConstantOrVariableCell(t0, t1, .opPutByValSlow) loadp JSCell::m_structure[t1], t2 - loadp 32[PB, PC, 8], t0 - arrayProfile(t2, t0, t3) + loadp 32[PB, PC, 8], t3 + arrayProfile(t2, t3, t0) btiz t2, HasArrayStorage, .opPutByValSlow loadis 16[PB, PC, 8], t0 loadConstantOrVariableInt32(t0, t2, .opPutByValSlow) @@ -1248,6 +1248,7 @@ _llint_op_put_by_val: dispatch(5) .opPutByValEmpty: + storeb 1, ArrayProfile::m_mayStoreToHole[t3] addi 1, ArrayStorage::m_numValuesInVector[t0] bib t2, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t0], .opPutByValStoreResult addi 1, t2, t1 diff --git a/Source/JavaScriptCore/runtime/Arguments.h b/Source/JavaScriptCore/runtime/Arguments.h index ad0e651ea..c9d0d503d 100644 --- a/Source/JavaScriptCore/runtime/Arguments.h +++ b/Source/JavaScriptCore/runtime/Arguments.h @@ -89,7 +89,7 @@ namespace JSC { } protected: - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; void finishCreation(CallFrame*); void finishCreation(CallFrame*, InlineCallFrame*); diff --git a/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h b/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h index 8d6e08256..e1d893b0b 100644 --- a/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h +++ b/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h @@ -34,7 +34,7 @@ namespace JSC { inline size_t IndexingHeader::preCapacity(Structure* structure) { - if (LIKELY(!(structure->indexingType() & HasArrayStorage))) + if (LIKELY(!hasArrayStorage(structure->indexingType()))) return 0; return arrayStorage()->m_indexBias; @@ -42,7 +42,7 @@ inline size_t IndexingHeader::preCapacity(Structure* structure) inline size_t IndexingHeader::indexingPayloadSizeInBytes(Structure* structure) { - if (LIKELY(!(structure->indexingType() & HasArrayStorage))) + if (LIKELY(!hasArrayStorage(structure->indexingType()))) return 0; return ArrayStorage::sizeFor(arrayStorage()->vectorLength()); diff --git a/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h index 94f08f31b..d6abcba99 100644 --- a/Source/JavaScriptCore/runtime/JSCell.h +++ b/Source/JavaScriptCore/runtime/JSCell.h @@ -33,6 +33,7 @@ #include "SlotVisitorInlineMethods.h" #include "WriteBarrier.h" #include <wtf/Noncopyable.h> +#include <wtf/TypeTraits.h> namespace JSC { @@ -308,18 +309,10 @@ namespace JSC { return isCell() ? asCell()->toObject(exec, globalObject) : toObjectSlowCase(exec, globalObject); } -#if COMPILER(CLANG) template<class T> struct NeedsDestructor { - static const bool value = !__has_trivial_destructor(T); + static const bool value = !WTF::HasTrivialDestructor<T>::value; }; -#else - // Write manual specializations for this struct template if you care about non-clang compilers. - template<class T> - struct NeedsDestructor { - static const bool value = true; - }; -#endif template<typename T> void* allocateCell(Heap& heap) diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp index a6993aabc..27f68e55b 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -482,6 +482,7 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) visitor.append(&thisObject->m_nameScopeStructure); visitor.append(&thisObject->m_argumentsStructure); visitor.append(&thisObject->m_arrayStructure); + visitor.append(&thisObject->m_arrayStructureForSlowPut); visitor.append(&thisObject->m_booleanObjectStructure); visitor.append(&thisObject->m_callbackConstructorStructure); visitor.append(&thisObject->m_callbackFunctionStructure); diff --git a/Source/JavaScriptCore/runtime/JSNotAnObject.h b/Source/JavaScriptCore/runtime/JSNotAnObject.h index 9980e4aab..3636c9bd4 100644 --- a/Source/JavaScriptCore/runtime/JSNotAnObject.h +++ b/Source/JavaScriptCore/runtime/JSNotAnObject.h @@ -62,7 +62,7 @@ namespace JSC { private: - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSObject::StructureFlags; + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames | JSObject::StructureFlags; // JSValue methods static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h index 8b52915b6..bf2dae20d 100644 --- a/Source/JavaScriptCore/runtime/JSObject.h +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -505,6 +505,25 @@ namespace JSC { // foo->attemptToInterceptPutByIndexOnHole(...); bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow); + // Ensure that the object is in a mode where it has array storage. Use + // this if you're about to perform actions that would have required the + // object to be converted to have array storage, if it didn't have it + // already. + ArrayStorage* ensureArrayStorage(JSGlobalData& globalData) + { + switch (structure()->indexingType()) { + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return m_butterfly->arrayStorage(); + + case ALL_BLANK_INDEXING_TYPES: + return createInitialArrayStorage(globalData); + + default: + ASSERT_NOT_REACHED(); + return 0; + } + } + static size_t offsetOfInlineStorage(); static ptrdiff_t butterflyOffset() @@ -565,25 +584,6 @@ namespace JSC { } } - // Ensure that the object is in a mode where it has array storage. Use - // this if you're about to perform actions that would have required the - // object to be converted to have array storage, if it didn't have it - // already. - ArrayStorage* ensureArrayStorage(JSGlobalData& globalData) - { - switch (structure()->indexingType()) { - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->arrayStorage(); - - case ALL_BLANK_INDEXING_TYPES: - return createInitialArrayStorage(globalData); - - default: - ASSERT_NOT_REACHED(); - return 0; - } - } - ArrayStorage* createArrayStorage(JSGlobalData&, unsigned length, unsigned vectorLength); ArrayStorage* createInitialArrayStorage(JSGlobalData&); diff --git a/Source/JavaScriptCore/runtime/JSString.h b/Source/JavaScriptCore/runtime/JSString.h index 7821b42b8..1500636f2 100644 --- a/Source/JavaScriptCore/runtime/JSString.h +++ b/Source/JavaScriptCore/runtime/JSString.h @@ -153,7 +153,7 @@ namespace JSC { static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) { - return Structure::create(globalData, globalObject, proto, TypeInfo(StringType, OverridesGetOwnPropertySlot), &s_info); + return Structure::create(globalData, globalObject, proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero), &s_info); } static size_t offsetOfLength() { return OBJECT_OFFSETOF(JSString, m_length); } |