diff options
Diffstat (limited to 'Source/JavaScriptCore')
220 files changed, 9873 insertions, 2628 deletions
diff --git a/Source/JavaScriptCore/API/JSObjectRef.cpp b/Source/JavaScriptCore/API/JSObjectRef.cpp index 491fa988f..c62efc60d 100644 --- a/Source/JavaScriptCore/API/JSObjectRef.cpp +++ b/Source/JavaScriptCore/API/JSObjectRef.cpp @@ -29,9 +29,9 @@ #include "JSObjectRefPrivate.h" #include "APICast.h" -#include "ButterflyInlineMethods.h" +#include "ButterflyInlines.h" #include "CodeBlock.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "DateConstructor.h" #include "ErrorConstructor.h" #include "FunctionConstructor.h" @@ -144,9 +144,9 @@ JSObjectRef JSObjectMakeArray(JSContextRef ctx, size_t argumentCount, const JSVa for (size_t i = 0; i < argumentCount; ++i) argList.append(toJS(exec, arguments[i])); - result = constructArray(exec, argList); + result = constructArray(exec, static_cast<ArrayAllocationProfile*>(0), argList); } else - result = constructEmptyArray(exec); + result = constructEmptyArray(exec, 0); if (exec->hadException()) { if (exception) diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt index 393db67c3..a77e9e451 100644 --- a/Source/JavaScriptCore/CMakeLists.txt +++ b/Source/JavaScriptCore/CMakeLists.txt @@ -40,6 +40,7 @@ SET(JavaScriptCore_SOURCES assembler/MacroAssembler.cpp assembler/LinkBuffer.cpp + bytecode/ArrayAllocationProfile.cpp bytecode/ArrayProfile.cpp bytecode/CallLinkInfo.cpp bytecode/CallLinkStatus.cpp @@ -103,6 +104,8 @@ SET(JavaScriptCore_SOURCES dfg/DFGVariableEventStream.cpp dfg/DFGValidate.cpp dfg/DFGVirtualRegisterAllocationPhase.cpp + + disassembler/Disassembler.cpp heap/BlockAllocator.cpp heap/CopiedSpace.cpp @@ -138,6 +141,7 @@ SET(JavaScriptCore_SOURCES interpreter/JSStack.cpp interpreter/VMInspector.cpp + jit/ClosureCallStubRoutine.cpp jit/ExecutableAllocator.cpp jit/HostCallReturnValue.cpp jit/GCAwareJITStubRoutine.cpp @@ -146,6 +150,7 @@ SET(JavaScriptCore_SOURCES jit/JITCall32_64.cpp jit/JITCall.cpp jit/JIT.cpp + jit/JITDisassembler.cpp jit/JITExceptions.cpp jit/JITOpcodes32_64.cpp jit/JITOpcodes.cpp @@ -353,11 +358,18 @@ IF (ENABLE_LLINT) ) TARGET_LINK_LIBRARIES(LLIntOffsetsExtractor ${WTF_LIBRARY_NAME}) + # The build system will execute asm.rb every time LLIntOffsetsExtractor's mtime is newer than + # LLIntAssembly.h's mtime. The problem we have here is: asm.rb has some built-in optimization + # that generates a checksum of the LLIntOffsetsExtractor binary, if the checksum of the new + # LLIntOffsetsExtractor matches, no output is generated. To make this target consistent and avoid + # running this command for every build, we artificially update LLIntAssembly.h's mtime (using touch) + # after every asm.rb run. ADD_CUSTOM_COMMAND( OUTPUT ${DERIVED_SOURCES_JAVASCRIPTCORE_DIR}/LLIntAssembly.h MAIN_DEPENDENCY ${JAVASCRIPTCORE_DIR}/offlineasm/asm.rb DEPENDS LLIntOffsetsExtractor ${LLINT_ASM} ${OFFLINE_ASM} COMMAND ${RUBY_EXECUTABLE} ${JAVASCRIPTCORE_DIR}/offlineasm/asm.rb ${JAVASCRIPTCORE_DIR}/llint/LowLevelInterpreter.asm $<TARGET_FILE:LLIntOffsetsExtractor> ${DERIVED_SOURCES_JAVASCRIPTCORE_DIR}/LLIntAssembly.h + COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${DERIVED_SOURCES_JAVASCRIPTCORE_DIR}/LLIntAssembly.h VERBATIM) # The explanation for not making LLIntAssembly.h part of the OBJECT_DEPENDS property of some of @@ -385,6 +397,43 @@ FOREACH (_file ${JavaScriptCore_LUT_FILES}) LIST(APPEND JavaScriptCore_HEADERS ${DERIVED_SOURCES_JAVASCRIPTCORE_DIR}/${_name}.lut.h) ENDFOREACH () +SET (JavaScriptCore_FORWARDING_HEADERS_DIRECTORIES + assembler + bytecode + collector/handles + debugger + heap + interpreter + jit + llint + parser + profiler + runtime + yarr +) + +SET (JavaScriptCore_FORWARDING_HEADERS_FILES + API/APICast.h + API/APIShims.h + API/JavaScript.h + API/JSBase.h + API/JSContextRef.h + API/JSContextRefPrivate.h + API/JSObjectRef.h + API/JSObjectRefPrivate.h + API/JSStringRef.h + API/JSStringRefCF.h + API/JSStringRefBSTR.h + API/JSValueRef.h + API/JavaScriptCore.h + API/JSRetainPtr.h + API/JSWeakObjectMapRefInternal.h + API/JSWeakObjectMapRefPrivate.h + API/JSRetainPtr.h + API/OpaqueJSString.h + API/WebKitAvailability.h +) + # GENERATOR 1-B: particular LUT creator (for 1 file only) GENERATE_HASH_LUT(${JAVASCRIPTCORE_DIR}/parser/Keywords.table ${DERIVED_SOURCES_JAVASCRIPTCORE_DIR}/Lexer.lut.h MAIN_DEPENDENCY) @@ -423,6 +472,8 @@ ENDIF () WEBKIT_INCLUDE_CONFIG_FILES_IF_EXISTS() +WEBKIT_CREATE_FORWARDING_HEADERS(JavaScriptCore DIRECTORIES ${JavaScriptCore_FORWARDING_HEADERS_DIRECTORIES} FILES ${JavaScriptCore_FORWARDING_HEADERS_FILES}) + ADD_SUBDIRECTORY(shell) diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog index 3c0e4ddae..aa92eade1 100644 --- a/Source/JavaScriptCore/ChangeLog +++ b/Source/JavaScriptCore/ChangeLog @@ -1,3 +1,1569 @@ +2012-11-21 Filip Pizlo <fpizlo@apple.com> + + Rename dataLog() and dataLogV() to dataLogF() and dataLogFV() + https://bugs.webkit.org/show_bug.cgi?id=103001 + + Rubber stamped by Dan Bernstein. + + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + * assembler/LinkBuffer.cpp: + (JSC::LinkBuffer::finalizeCodeWithDisassembly): + (JSC::LinkBuffer::dumpLinkStatistics): + (JSC::LinkBuffer::dumpCode): + * assembler/LinkBuffer.h: + (JSC): + * assembler/SH4Assembler.h: + (JSC::SH4Assembler::vprintfStdoutInstr): + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::dumpBytecodeCommentAndNewLine): + (JSC::CodeBlock::printUnaryOp): + (JSC::CodeBlock::printBinaryOp): + (JSC::CodeBlock::printConditionalJump): + (JSC::CodeBlock::printGetByIdOp): + (JSC::dumpStructure): + (JSC::dumpChain): + (JSC::CodeBlock::printGetByIdCacheStatus): + (JSC::CodeBlock::printCallOp): + (JSC::CodeBlock::printPutByIdOp): + (JSC::CodeBlock::printStructure): + (JSC::CodeBlock::printStructures): + (JSC::CodeBlock::dump): + (JSC::CodeBlock::dumpStatistics): + (JSC::CodeBlock::finalizeUnconditionally): + (JSC::CodeBlock::resetStubInternal): + (JSC::CodeBlock::reoptimize): + (JSC::ProgramCodeBlock::jettison): + (JSC::EvalCodeBlock::jettison): + (JSC::FunctionCodeBlock::jettison): + (JSC::CodeBlock::shouldOptimizeNow): + (JSC::CodeBlock::tallyFrequentExitSites): + (JSC::CodeBlock::dumpValueProfiles): + * bytecode/Opcode.cpp: + (JSC::OpcodeStats::~OpcodeStats): + * bytecode/SamplingTool.cpp: + (JSC::SamplingFlags::stop): + (JSC::SamplingRegion::dumpInternal): + (JSC::SamplingTool::dump): + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::initialize): + (JSC::DFG::AbstractState::endBasicBlock): + (JSC::DFG::AbstractState::mergeStateAtTail): + (JSC::DFG::AbstractState::mergeToSuccessors): + * dfg/DFGAbstractValue.h: + (JSC::DFG::AbstractValue::dump): + * dfg/DFGArgumentsSimplificationPhase.cpp: + (JSC::DFG::ArgumentsSimplificationPhase::run): + * dfg/DFGByteCodeParser.cpp: + (JSC::DFG::ByteCodeParser::injectLazyOperandSpeculation): + (JSC::DFG::ByteCodeParser::getPredictionWithoutOSRExit): + (JSC::DFG::ByteCodeParser::getArrayModeAndEmitChecks): + (JSC::DFG::ByteCodeParser::makeSafe): + (JSC::DFG::ByteCodeParser::makeDivSafe): + (JSC::DFG::ByteCodeParser::handleCall): + (JSC::DFG::ByteCodeParser::handleInlining): + (JSC::DFG::ByteCodeParser::parseBlock): + (JSC::DFG::ByteCodeParser::processPhiStack): + (JSC::DFG::ByteCodeParser::linkBlock): + (JSC::DFG::ByteCodeParser::InlineStackEntry::InlineStackEntry): + (JSC::DFG::ByteCodeParser::parseCodeBlock): + (JSC::DFG::ByteCodeParser::parse): + * dfg/DFGCFAPhase.cpp: + (JSC::DFG::CFAPhase::performBlockCFA): + (JSC::DFG::CFAPhase::performForwardCFA): + * dfg/DFGCFGSimplificationPhase.cpp: + (JSC::DFG::CFGSimplificationPhase::run): + (JSC::DFG::CFGSimplificationPhase::fixPossibleGetLocal): + (JSC::DFG::CFGSimplificationPhase::fixPhis): + (JSC::DFG::CFGSimplificationPhase::fixJettisonedPredecessors): + (JSC::DFG::CFGSimplificationPhase::removePotentiallyDeadPhiReference): + (JSC::DFG::CFGSimplificationPhase::mergeBlocks): + * dfg/DFGCSEPhase.cpp: + (JSC::DFG::CSEPhase::endIndexForPureCSE): + (JSC::DFG::CSEPhase::setReplacement): + (JSC::DFG::CSEPhase::eliminate): + (JSC::DFG::CSEPhase::performNodeCSE): + * dfg/DFGCapabilities.cpp: + (JSC::DFG::debugFail): + * dfg/DFGConstantFoldingPhase.cpp: + (JSC::DFG::ConstantFoldingPhase::foldConstants): + (JSC::DFG::ConstantFoldingPhase::paintUnreachableCode): + * dfg/DFGDisassembler.cpp: + (JSC::DFG::Disassembler::dump): + * dfg/DFGDriver.cpp: + (JSC::DFG::compile): + * dfg/DFGFixupPhase.cpp: + (JSC::DFG::FixupPhase::fixupNode): + (JSC::DFG::FixupPhase::fixDoubleEdge): + * dfg/DFGGraph.cpp: + (JSC::DFG::printWhiteSpace): + (JSC::DFG::Graph::dumpCodeOrigin): + (JSC::DFG::Graph::dump): + (JSC::DFG::Graph::dumpBlockHeader): + (JSC::DFG::Graph::predictArgumentTypes): + * dfg/DFGJITCompiler.cpp: + (JSC::DFG::JITCompiler::link): + * dfg/DFGOSREntry.cpp: + (JSC::DFG::prepareOSREntry): + * dfg/DFGOSRExitCompiler.cpp: + * dfg/DFGOSRExitCompiler32_64.cpp: + (JSC::DFG::OSRExitCompiler::compileExit): + * dfg/DFGOSRExitCompiler64.cpp: + (JSC::DFG::OSRExitCompiler::compileExit): + * dfg/DFGOperations.cpp: + * dfg/DFGPhase.cpp: + (JSC::DFG::Phase::beginPhase): + * dfg/DFGPhase.h: + (JSC::DFG::runAndLog): + * dfg/DFGPredictionPropagationPhase.cpp: + (JSC::DFG::PredictionPropagationPhase::propagate): + (JSC::DFG::PredictionPropagationPhase::propagateForward): + (JSC::DFG::PredictionPropagationPhase::propagateBackward): + (JSC::DFG::PredictionPropagationPhase::doRoundOfDoubleVoting): + * dfg/DFGRegisterBank.h: + (JSC::DFG::RegisterBank::dump): + * dfg/DFGScoreBoard.h: + (JSC::DFG::ScoreBoard::use): + (JSC::DFG::ScoreBoard::dump): + * dfg/DFGSlowPathGenerator.h: + (JSC::DFG::SlowPathGenerator::generate): + * dfg/DFGSpeculativeJIT.cpp: + (JSC::DFG::SpeculativeJIT::terminateSpeculativeExecution): + (JSC::DFG::SpeculativeJIT::terminateSpeculativeExecutionWithConditionalDirection): + (JSC::DFG::SpeculativeJIT::runSlowPathGenerators): + (JSC::DFG::SpeculativeJIT::dump): + (JSC::DFG::SpeculativeJIT::checkConsistency): + (JSC::DFG::SpeculativeJIT::compile): + (JSC::DFG::SpeculativeJIT::checkGeneratedTypeForToInt32): + * dfg/DFGSpeculativeJIT32_64.cpp: + (JSC::DFG::SpeculativeJIT::fillSpeculateIntInternal): + (JSC::DFG::SpeculativeJIT::fillSpeculateDouble): + (JSC::DFG::SpeculativeJIT::fillSpeculateCell): + (JSC::DFG::SpeculativeJIT::fillSpeculateBoolean): + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::fillSpeculateIntInternal): + (JSC::DFG::SpeculativeJIT::fillSpeculateDouble): + (JSC::DFG::SpeculativeJIT::fillSpeculateCell): + (JSC::DFG::SpeculativeJIT::fillSpeculateBoolean): + * dfg/DFGStructureCheckHoistingPhase.cpp: + (JSC::DFG::StructureCheckHoistingPhase::run): + * dfg/DFGValidate.cpp: + (Validate): + (JSC::DFG::Validate::reportValidationContext): + (JSC::DFG::Validate::dumpData): + (JSC::DFG::Validate::dumpGraphIfAppropriate): + * dfg/DFGVariableEventStream.cpp: + (JSC::DFG::VariableEventStream::logEvent): + (JSC::DFG::VariableEventStream::reconstruct): + * dfg/DFGVirtualRegisterAllocationPhase.cpp: + (JSC::DFG::VirtualRegisterAllocationPhase::run): + * heap/Heap.cpp: + * heap/HeapStatistics.cpp: + (JSC::HeapStatistics::logStatistics): + (JSC::HeapStatistics::showObjectStatistics): + * heap/MarkStack.h: + * heap/MarkedBlock.h: + * heap/SlotVisitor.cpp: + (JSC::SlotVisitor::validate): + * interpreter/CallFrame.cpp: + (JSC::CallFrame::dumpCaller): + * interpreter/Interpreter.cpp: + (JSC::Interpreter::dumpRegisters): + * jit/JIT.cpp: + (JSC::JIT::privateCompileMainPass): + (JSC::JIT::privateCompileSlowCases): + (JSC::JIT::privateCompile): + * jit/JITDisassembler.cpp: + (JSC::JITDisassembler::dump): + (JSC::JITDisassembler::dumpForInstructions): + * jit/JITStubRoutine.h: + (JSC): + * jit/JITStubs.cpp: + (JSC::DEFINE_STUB_FUNCTION): + * jit/JumpReplacementWatchpoint.cpp: + (JSC::JumpReplacementWatchpoint::fireInternal): + * llint/LLIntExceptions.cpp: + (JSC::LLInt::interpreterThrowInCaller): + (JSC::LLInt::returnToThrow): + (JSC::LLInt::callToThrow): + * llint/LLIntSlowPaths.cpp: + (JSC::LLInt::llint_trace_operand): + (JSC::LLInt::llint_trace_value): + (JSC::LLInt::LLINT_SLOW_PATH_DECL): + (JSC::LLInt::traceFunctionPrologue): + (JSC::LLInt::jitCompileAndSetHeuristics): + (JSC::LLInt::entryOSR): + (JSC::LLInt::handleHostCall): + (JSC::LLInt::setUpCall): + * profiler/Profile.cpp: + (JSC::Profile::debugPrintData): + (JSC::Profile::debugPrintDataSampleStyle): + * profiler/ProfileNode.cpp: + (JSC::ProfileNode::debugPrintData): + (JSC::ProfileNode::debugPrintDataSampleStyle): + * runtime/JSGlobalData.cpp: + (JSC::JSGlobalData::dumpRegExpTrace): + * runtime/RegExp.cpp: + (JSC::RegExp::matchCompareWithInterpreter): + * runtime/SamplingCounter.cpp: + (JSC::AbstractSamplingCounter::dump): + * runtime/Structure.cpp: + (JSC::Structure::dumpStatistics): + (JSC::PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger): + * tools/CodeProfile.cpp: + (JSC::CodeProfile::report): + * tools/ProfileTreeNode.h: + (JSC::ProfileTreeNode::dumpInternal): + * yarr/YarrInterpreter.cpp: + (JSC::Yarr::ByteCompiler::dumpDisjunction): + +2012-11-21 Filip Pizlo <fpizlo@apple.com> + + It should be possible to say disassemble(stuff) instead of having to say if (!tryToDisassemble(stuff)) dataLog("I failed") + https://bugs.webkit.org/show_bug.cgi?id=103010 + + Reviewed by Anders Carlsson. + + You can still say tryToDisassemble(), which will tell you if it failed; you can then + decide what to do instead. But it's better to say disassemble(), which will just print + the instruction ranges if tryToDisassemble() failed. This is particularly appropriate + since that's what all previous users of tryToDisassemble() would have done in some + form or another. + + * CMakeLists.txt: + * GNUmakefile.list.am: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: + * JavaScriptCore.xcodeproj/project.pbxproj: + * Target.pri: + * assembler/LinkBuffer.cpp: + (JSC::LinkBuffer::finalizeCodeWithDisassembly): + * dfg/DFGDisassembler.cpp: + (JSC::DFG::Disassembler::dumpDisassembly): + * disassembler/Disassembler.cpp: Added. + (JSC): + (JSC::disassemble): + * disassembler/Disassembler.h: + (JSC): + * jit/JITDisassembler.cpp: + (JSC::JITDisassembler::dumpDisassembly): + +2012-11-21 Filip Pizlo <fpizlo@apple.com> + + dumpOperands() claims that it needs a non-const Operands& when that is completely false + https://bugs.webkit.org/show_bug.cgi?id=103005 + + Reviewed by Eric Carlson. + + * bytecode/Operands.h: + (JSC::dumpOperands): + (JSC): + +2012-11-20 Filip Pizlo <fpizlo@apple.com> + + Baseline JIT's disassembly should be just as pretty as the DFG's + https://bugs.webkit.org/show_bug.cgi?id=102873 + + Reviewed by Sam Weinig. + + Integrated the CodeBlock's bytecode dumper with the JIT's disassembler. Also fixed + some type goof-ups (instructions are not in a Vector<Instruction> so using a Vector + iterator makes no sense) and stream-lined some things (you don't actually need a + full-fledged ExecState* to dump bytecode). + + * CMakeLists.txt: + * GNUmakefile.list.am: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: + * JavaScriptCore.xcodeproj/project.pbxproj: + * Target.pri: + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::printUnaryOp): + (JSC::CodeBlock::printBinaryOp): + (JSC::CodeBlock::printConditionalJump): + (JSC::CodeBlock::printGetByIdOp): + (JSC::CodeBlock::printCallOp): + (JSC::CodeBlock::printPutByIdOp): + (JSC::CodeBlock::dump): + (JSC): + (JSC::CodeBlock::CodeBlock): + * bytecode/CodeBlock.h: + (CodeBlock): + * interpreter/Interpreter.cpp: + (JSC::Interpreter::dumpCallFrame): + * jit/JIT.cpp: + (JSC::JIT::privateCompileMainPass): + (JSC::JIT::privateCompileSlowCases): + (JSC::JIT::privateCompile): + * jit/JIT.h: + (JIT): + * jit/JITDisassembler.cpp: Added. + (JSC): + (JSC::JITDisassembler::JITDisassembler): + (JSC::JITDisassembler::~JITDisassembler): + (JSC::JITDisassembler::dump): + (JSC::JITDisassembler::dumpForInstructions): + (JSC::JITDisassembler::dumpDisassembly): + * jit/JITDisassembler.h: Added. + (JSC): + (JITDisassembler): + (JSC::JITDisassembler::setStartOfCode): + (JSC::JITDisassembler::setForBytecodeMainPath): + (JSC::JITDisassembler::setForBytecodeSlowPath): + (JSC::JITDisassembler::setEndOfSlowPath): + (JSC::JITDisassembler::setEndOfCode): + +2012-11-21 Daniel Bates <dbates@webkit.org> + + JavaScript fails to concatenate large strings + <https://bugs.webkit.org/show_bug.cgi?id=102963> + + Reviewed by Michael Saboff. + + Fixes an issue where we inadvertently didn't check the length of + a JavaScript string for overflow. + + * runtime/Operations.h: + (JSC::jsString): + (JSC::jsStringFromArguments): + +2012-11-20 Filip Pizlo <fpizlo@apple.com> + + DFG should be able to cache closure calls (part 2/2) + https://bugs.webkit.org/show_bug.cgi?id=102662 + + Reviewed by Gavin Barraclough. + + Added caching of calls where the JSFunction* varies, but the Structure* and ExecutableBase* + stay the same. This is accomplished by replacing the branch that compares against a constant + JSFunction* with a jump to a closure call stub. The closure call stub contains a fast path, + and jumps slow directly to the virtual call thunk. + + Looks like a 1% win on V8v7. + + * CMakeLists.txt: + * GNUmakefile.list.am: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: + * JavaScriptCore.xcodeproj/project.pbxproj: + * Target.pri: + * bytecode/CallLinkInfo.cpp: + (JSC::CallLinkInfo::unlink): + * bytecode/CallLinkInfo.h: + (CallLinkInfo): + (JSC::CallLinkInfo::isLinked): + (JSC::getCallLinkInfoBytecodeIndex): + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::finalizeUnconditionally): + (JSC): + (JSC::CodeBlock::findClosureCallForReturnPC): + (JSC::CodeBlock::bytecodeOffset): + (JSC::CodeBlock::codeOriginForReturn): + * bytecode/CodeBlock.h: + (JSC::CodeBlock::getCallLinkInfo): + (CodeBlock): + (JSC::CodeBlock::isIncomingCallAlreadyLinked): + * dfg/DFGJITCompiler.cpp: + (JSC::DFG::JITCompiler::link): + * dfg/DFGJITCompiler.h: + (JSC::DFG::JITCompiler::addJSCall): + (JSC::DFG::JITCompiler::JSCallRecord::JSCallRecord): + (JSCallRecord): + * dfg/DFGOperations.cpp: + * dfg/DFGOperations.h: + * dfg/DFGRepatch.cpp: + (JSC::DFG::linkSlowFor): + (DFG): + (JSC::DFG::dfgLinkFor): + (JSC::DFG::dfgLinkSlowFor): + (JSC::DFG::dfgLinkClosureCall): + * dfg/DFGRepatch.h: + (DFG): + * dfg/DFGSpeculativeJIT32_64.cpp: + (JSC::DFG::SpeculativeJIT::emitCall): + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::emitCall): + * dfg/DFGThunks.cpp: + (DFG): + (JSC::DFG::linkClosureCallThunkGenerator): + * dfg/DFGThunks.h: + (DFG): + * heap/Heap.h: + (Heap): + (JSC::Heap::jitStubRoutines): + * heap/JITStubRoutineSet.h: + (JSC::JITStubRoutineSet::size): + (JSC::JITStubRoutineSet::at): + (JITStubRoutineSet): + * jit/ClosureCallStubRoutine.cpp: Added. + (JSC): + (JSC::ClosureCallStubRoutine::ClosureCallStubRoutine): + (JSC::ClosureCallStubRoutine::~ClosureCallStubRoutine): + (JSC::ClosureCallStubRoutine::markRequiredObjectsInternal): + * jit/ClosureCallStubRoutine.h: Added. + (JSC): + (ClosureCallStubRoutine): + (JSC::ClosureCallStubRoutine::structure): + (JSC::ClosureCallStubRoutine::executable): + (JSC::ClosureCallStubRoutine::codeOrigin): + * jit/GCAwareJITStubRoutine.cpp: + (JSC::GCAwareJITStubRoutine::GCAwareJITStubRoutine): + * jit/GCAwareJITStubRoutine.h: + (GCAwareJITStubRoutine): + (JSC::GCAwareJITStubRoutine::isClosureCall): + * jit/JIT.cpp: + (JSC::JIT::privateCompile): + +2012-11-20 Filip Pizlo <fpizlo@apple.com> + + DFG should be able to cache closure calls (part 1/2) + https://bugs.webkit.org/show_bug.cgi?id=102662 + + Reviewed by Gavin Barraclough. + + Add ability to revert a jump replacement back to + branchPtrWithPatch(Condition, RegisterID, TrustedImmPtr). This is meant to be + a mandatory piece of functionality for all assemblers. I also renamed some of + the functions for reverting jump replacements back to + patchableBranchPtrWithPatch(Condition, Address, TrustedImmPtr), so as to avoid + confusion. + + * assembler/ARMv7Assembler.h: + (JSC::ARMv7Assembler::BadReg): + (ARMv7Assembler): + (JSC::ARMv7Assembler::revertJumpTo_movT3): + * assembler/LinkBuffer.h: + (JSC): + * assembler/MacroAssemblerARMv7.h: + (JSC::MacroAssemblerARMv7::startOfBranchPtrWithPatchOnRegister): + (MacroAssemblerARMv7): + (JSC::MacroAssemblerARMv7::revertJumpReplacementToBranchPtrWithPatch): + (JSC::MacroAssemblerARMv7::startOfPatchableBranchPtrWithPatchOnAddress): + * assembler/MacroAssemblerX86.h: + (JSC::MacroAssemblerX86::startOfBranchPtrWithPatchOnRegister): + (MacroAssemblerX86): + (JSC::MacroAssemblerX86::startOfPatchableBranchPtrWithPatchOnAddress): + (JSC::MacroAssemblerX86::revertJumpReplacementToBranchPtrWithPatch): + * assembler/MacroAssemblerX86_64.h: + (JSC::MacroAssemblerX86_64::startOfBranchPtrWithPatchOnRegister): + (JSC::MacroAssemblerX86_64::startOfPatchableBranchPtrWithPatchOnAddress): + (MacroAssemblerX86_64): + (JSC::MacroAssemblerX86_64::revertJumpReplacementToBranchPtrWithPatch): + * assembler/RepatchBuffer.h: + (JSC::RepatchBuffer::startOfBranchPtrWithPatchOnRegister): + (RepatchBuffer): + (JSC::RepatchBuffer::startOfPatchableBranchPtrWithPatchOnAddress): + (JSC::RepatchBuffer::revertJumpReplacementToBranchPtrWithPatch): + * assembler/X86Assembler.h: + (JSC::X86Assembler::revertJumpTo_cmpl_ir_force32): + (X86Assembler): + * dfg/DFGRepatch.cpp: + (JSC::DFG::replaceWithJump): + (JSC::DFG::dfgResetGetByID): + (JSC::DFG::dfgResetPutByID): + +2012-11-20 Yong Li <yoli@rim.com> + + [ARMv7] Neither linkCall() nor linkPointer() should flush code. + https://bugs.webkit.org/show_bug.cgi?id=99213 + + Reviewed by George Staikos. + + LinkBuffer doesn't need to flush code during linking. It will + eventually flush the whole executable. Fixing this gives >%5 + sunspider boost (on QNX). + + Also make replaceWithLoad() and replaceWithAddressComputation() flush + only when necessary. + + * assembler/ARMv7Assembler.h: + (JSC::ARMv7Assembler::linkCall): + (JSC::ARMv7Assembler::linkPointer): + (JSC::ARMv7Assembler::relinkCall): + (JSC::ARMv7Assembler::repatchInt32): + (JSC::ARMv7Assembler::repatchPointer): + (JSC::ARMv7Assembler::replaceWithLoad): Flush only after it did write. + (JSC::ARMv7Assembler::replaceWithAddressComputation): Flush only after it did write. + (JSC::ARMv7Assembler::setInt32): + (JSC::ARMv7Assembler::setPointer): + +2012-11-19 Filip Pizlo <fpizlo@apple.com> + + Remove support for ARMv7 errata from the jump code + https://bugs.webkit.org/show_bug.cgi?id=102759 + + Reviewed by Oliver Hunt. + + The jump replacement code was wrong to begin with since it wasn't doing + a cache flush on the inserted padding. And, to my knowledge, we don't need + this anymore, so this patch removes all errata code from the ARMv7 port. + + * assembler/ARMv7Assembler.h: + (JSC::ARMv7Assembler::computeJumpType): + (JSC::ARMv7Assembler::replaceWithJump): + (JSC::ARMv7Assembler::maxJumpReplacementSize): + (JSC::ARMv7Assembler::canBeJumpT3): + (JSC::ARMv7Assembler::canBeJumpT4): + +2012-11-19 Patrick Gansterer <paroga@webkit.org> + + [CMake] Create JavaScriptCore ForwardingHeaders + https://bugs.webkit.org/show_bug.cgi?id=92665 + + Reviewed by Brent Fulgham. + + When using CMake to build the Windows port, we need + to generate the forwarding headers with it too. + + * CMakeLists.txt: + +2012-11-19 Kihong Kwon <kihong.kwon@samsung.com> + + Add PROXIMITY_EVENTS feature + https://bugs.webkit.org/show_bug.cgi?id=102658 + + Reviewed by Kentaro Hara. + + Add PROXIMITY_EVENTS feature to xcode project for JavaScriptCore. + + * Configurations/FeatureDefines.xcconfig: + +2012-11-18 Dan Bernstein <mitz@apple.com> + + Try to fix the DFG build after r135099. + + * dfg/DFGCommon.h: + (JSC::DFG::shouldShowDisassembly): + +2012-11-18 Filip Pizlo <fpizlo@apple.com> + + Unreviewed, build fix for !ENABLE(DFG_JIT). + + * dfg/DFGCommon.h: + (JSC::DFG::shouldShowDisassembly): + (DFG): + +2012-11-18 Filip Pizlo <fpizlo@apple.com> + + JSC should have more logging in structure-related code + https://bugs.webkit.org/show_bug.cgi?id=102630 + + Reviewed by Simon Fraser. + + - JSValue::description() now tells you if something is a structure, and if so, + what kind of structure it is. + + - Jettisoning logic now tells you why things are being jettisoned. + + - It's now possible to turn off GC-triggered jettisoning entirely. + + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::finalizeUnconditionally): + (JSC::CodeBlock::reoptimize): + (JSC::ProgramCodeBlock::jettison): + (JSC::EvalCodeBlock::jettison): + (JSC::FunctionCodeBlock::jettison): + * bytecode/CodeBlock.h: + (JSC::CodeBlock::shouldImmediatelyAssumeLivenessDuringScan): + * runtime/JSValue.cpp: + (JSC::JSValue::description): + * runtime/Options.h: + (JSC): + +2012-11-18 Filip Pizlo <fpizlo@apple.com> + + DFG constant folding phase should say 'changed = true' whenever it changes the graph + https://bugs.webkit.org/show_bug.cgi?id=102550 + + Rubber stamped by Mark Hahnenberg. + + * dfg/DFGConstantFoldingPhase.cpp: + (JSC::DFG::ConstantFoldingPhase::foldConstants): + +2012-11-17 Elliott Sprehn <esprehn@chromium.org> + + Expose JSObject removeDirect and PrivateName to WebCore + https://bugs.webkit.org/show_bug.cgi?id=102546 + + Reviewed by Geoffrey Garen. + + Export removeDirect for use in WebCore so JSDependentRetained works. + + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + +2012-11-16 Filip Pizlo <fpizlo@apple.com> + + Given a PutById or GetById with a proven structure, the DFG should be able to emit a PutByOffset or GetByOffset instead + https://bugs.webkit.org/show_bug.cgi?id=102327 + + Reviewed by Mark Hahnenberg. + + If the profiler tells us that a GetById or PutById may be polymorphic but our + control flow analysis proves that it isn't, we should trust the control flow + analysis over the profiler. This arises in cases where GetById or PutById were + inlined: the inlined function may have been called from other places that led + to polymorphism, but in the current inlined context, there is no polymorphism. + + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::dump): + * bytecode/GetByIdStatus.cpp: + (JSC::GetByIdStatus::computeFor): + (JSC): + * bytecode/GetByIdStatus.h: + (JSC::GetByIdStatus::GetByIdStatus): + (GetByIdStatus): + * bytecode/PutByIdStatus.cpp: + (JSC::PutByIdStatus::computeFor): + (JSC): + * bytecode/PutByIdStatus.h: + (JSC): + (JSC::PutByIdStatus::PutByIdStatus): + (PutByIdStatus): + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::execute): + * dfg/DFGAbstractValue.h: + (JSC::DFG::AbstractValue::bestProvenStructure): + (AbstractValue): + * dfg/DFGConstantFoldingPhase.cpp: + (JSC::DFG::ConstantFoldingPhase::foldConstants): + (JSC::DFG::ConstantFoldingPhase::addStructureTransitionCheck): + (ConstantFoldingPhase): + * dfg/DFGNode.h: + (JSC::DFG::Node::convertToGetByOffset): + (Node): + (JSC::DFG::Node::convertToPutByOffset): + (JSC::DFG::Node::hasStorageResult): + * runtime/JSGlobalObject.h: + (JSC::Structure::prototypeChain): + (JSC): + (JSC::Structure::isValid): + * runtime/Operations.h: + (JSC::isPrototypeChainNormalized): + (JSC): + * runtime/Structure.h: + (Structure): + (JSC::Structure::transitionDidInvolveSpecificValue): + +2012-11-16 Tony Chang <tony@chromium.org> + + Remove ENABLE_CSS_HIERARCHIES since it's no longer in use + https://bugs.webkit.org/show_bug.cgi?id=102554 + + Reviewed by Andreas Kling. + + As mentioned in https://bugs.webkit.org/show_bug.cgi?id=79939#c41 , + we're going to revist this feature once additional vendor support is + achieved. + + * Configurations/FeatureDefines.xcconfig: + +2012-11-16 Patrick Gansterer <paroga@webkit.org> + + Build fix for WinCE after r133688. + + Use numeric_limits<uint32_t>::max() instead of UINT32_MAX. + + * runtime/CodeCache.h: + (JSC::CacheMap::CacheMap): + +2012-11-15 Filip Pizlo <fpizlo@apple.com> + + ClassInfo.h should have correct indentation. + + Rubber stamped by Mark Hahnenberg. + + ClassInfo.h had some true creativity in its use of whitespace. Some things within + the namespace were indented four spaces and others where not. One #define had its + contents indented four spaces, while another didn't. I applied the following rule: + + - Non-macro things in the namespace should not be indented (that's our current + accepted practice). + + - Macros should never be indented but if they are multi-line then their subsequent + bodies should be indented four spaces. I believe that is consistent with what we + do elsewhere. + + * runtime/ClassInfo.h: + (JSC): + (MethodTable): + (ClassInfo): + (JSC::ClassInfo::propHashTable): + (JSC::ClassInfo::isSubClassOf): + (JSC::ClassInfo::hasStaticProperties): + +2012-11-15 Filip Pizlo <fpizlo@apple.com> + + DFG should copy propagate trivially no-op ConvertThis + https://bugs.webkit.org/show_bug.cgi?id=102445 + + Reviewed by Oliver Hunt. + + Copy propagation is always a good thing, since it reveals must-alias relationships + to the CFA and CSE. This accomplishes copy propagation for ConvertThis by first + converting it to an Identity node (which is done by the constant folder since it + has access to CFA results) and then performing substitution of references to + Identity with references to Identity's child in the CSE. + + I'm not aiming for a big speed-up here; I just think that this will be useful for + the work on https://bugs.webkit.org/show_bug.cgi?id=102327. + + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::execute): + * dfg/DFGCSEPhase.cpp: + (JSC::DFG::CSEPhase::performNodeCSE): + * dfg/DFGConstantFoldingPhase.cpp: + (JSC::DFG::ConstantFoldingPhase::foldConstants): + * dfg/DFGNodeType.h: + (DFG): + * dfg/DFGPredictionPropagationPhase.cpp: + (JSC::DFG::PredictionPropagationPhase::propagate): + * dfg/DFGSpeculativeJIT32_64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + +2012-11-15 Filip Pizlo <fpizlo@apple.com> + + CallData.h should have correct indentation. + + Rubber stamped by Mark Hahneberg. + + * runtime/CallData.h: + (JSC): + +2012-11-15 Filip Pizlo <fpizlo@apple.com> + + Remove methodCallDummy since it is not used anymore. + + Rubber stamped by Mark Hahnenberg. + + * runtime/JSGlobalObject.cpp: + (JSC::JSGlobalObject::reset): + (JSC): + (JSC::JSGlobalObject::visitChildren): + * runtime/JSGlobalObject.h: + (JSGlobalObject): + +2012-11-14 Filip Pizlo <fpizlo@apple.com> + + Structure should be able to easily tell if the prototype chain might intercept a store + https://bugs.webkit.org/show_bug.cgi?id=102326 + + Reviewed by Geoffrey Garen. + + This improves our ability to reason about the correctness of the more optimized + prototype chain walk in JSObject::put(), while also making it straight forward to + check if the prototype chain will do strange things to a property store by just + looking at the structure. + + * runtime/JSObject.cpp: + (JSC::JSObject::put): + * runtime/Structure.cpp: + (JSC::Structure::prototypeChainMayInterceptStoreTo): + (JSC): + * runtime/Structure.h: + (Structure): + +2012-11-15 Thiago Marcos P. Santos <thiago.santos@intel.com> + + [CMake] Do not regenerate LLIntAssembly.h on every incremental build + https://bugs.webkit.org/show_bug.cgi?id=102248 + + Reviewed by Kenneth Rohde Christiansen. + + Update LLIntAssembly.h's mtime after running asm.rb to make the build + system dependency tracking consistent. + + * CMakeLists.txt: + +2012-11-15 Thiago Marcos P. Santos <thiago.santos@intel.com> + + Fix compiler warnings about signed/unsigned comparison on i386 + https://bugs.webkit.org/show_bug.cgi?id=102249 + + Reviewed by Kenneth Rohde Christiansen. + + Add casting to unsigned to shut up gcc warnings. Build was broken on + JSVALUE32_64 ports compiling with -Werror. + + * llint/LLIntData.cpp: + (JSC::LLInt::Data::performAssertions): + +2012-11-14 Brent Fulgham <bfulgham@webkit.org> + + [Windows, WinCairo] Unreviewed build fix. + + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + Missed one of the exports that was part of the WebKit2.def. + +2012-11-14 Brent Fulgham <bfulgham@webkit.org> + + [Windows, WinCairo] Correct build failure. + https://bugs.webkit.org/show_bug.cgi?id=102302 + + WebCore symbols were mistakenly added to the JavaScriptCore + library definition file. + + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: Remove + WebCore symbols that were incorrectly added to the export file. + +2012-11-14 Mark Lam <mark.lam@apple.com> + + Change JSEventListener::m_jsFunction to be a weak ref. + https://bugs.webkit.org/show_bug.cgi?id=101989. + + Reviewed by Geoffrey Garen. + + Added infrastructure for scanning weak ref slots. + + * heap/SlotVisitor.cpp: Added #include "SlotVisitorInlines.h". + * heap/SlotVisitor.h: + (SlotVisitor): Added SlotVisitor::appendUnbarrieredWeak(). + * heap/SlotVisitorInlines.h: Added #include "Weak.h". + (JSC::SlotVisitor::appendUnbarrieredWeak): Added. + * heap/Weak.h: + (JSC::operator==): Added operator==() for Weak. + * runtime/JSCell.h: Removed #include "SlotVisitorInlines.h". + * runtime/JSObject.h: Added #include "SlotVisitorInlines.h". + +2012-11-14 Filip Pizlo <fpizlo@apple.com> + + Read-only properties created with putDirect() should tell the structure that there are read-only properties + https://bugs.webkit.org/show_bug.cgi?id=102292 + + Reviewed by Gavin Barraclough. + + This mostly affects things like function.length. + + * runtime/JSObject.h: + (JSC::JSObject::putDirectInternal): + +2012-11-13 Filip Pizlo <fpizlo@apple.com> + + Don't access Node& after adding nodes to the graph. + https://bugs.webkit.org/show_bug.cgi?id=102005 + + Reviewed by Oliver Hunt. + + * dfg/DFGFixupPhase.cpp: + (JSC::DFG::FixupPhase::fixupNode): + +2012-11-14 Valery Ignatyev <valery.ignatyev@ispras.ru> + + Replace (typeof(x) != <"object", "undefined", ...>) with + !(typeof(x) == <"object",..>). Later is_object, is_<...> bytecode operation + will be used. + + https://bugs.webkit.org/show_bug.cgi?id=98893 + + Reviewed by Filip Pizlo. + + This eliminates expensive typeof implementation and + allows to use DFG optimizations, which doesn't support 'typeof'. + + * bytecompiler/NodesCodegen.cpp: + (JSC::BinaryOpNode::emitBytecode): + +2012-11-14 Peter Gal <galpeter@inf.u-szeged.hu> + + [Qt][ARM]REGRESSION(r133985): It broke the build + https://bugs.webkit.org/show_bug.cgi?id=101740 + + Reviewed by Csaba Osztrogonác. + + Changed the emitGenericContiguousPutByVal to accept the additional IndexingType argument. + This information was passed as a template parameter. + + * jit/JIT.h: + (JSC::JIT::emitInt32PutByVal): + (JSC::JIT::emitDoublePutByVal): + (JSC::JIT::emitContiguousPutByVal): + (JIT): + * jit/JITPropertyAccess.cpp: + (JSC::JIT::emitGenericContiguousPutByVal): + * jit/JITPropertyAccess32_64.cpp: + (JSC::JIT::emitGenericContiguousPutByVal): + +2012-11-14 Peter Gal <galpeter@inf.u-szeged.hu> + + Fix the MIPS build after r134332 + https://bugs.webkit.org/show_bug.cgi?id=102227 + + Reviewed by Csaba Osztrogonác. + + Added missing methods for the MacroAssemblerMIPS, based on the MacroAssemblerARMv7. + + * assembler/MacroAssemblerMIPS.h: + (JSC::MacroAssemblerMIPS::canJumpReplacePatchableBranchPtrWithPatch): + (MacroAssemblerMIPS): + (JSC::MacroAssemblerMIPS::startOfPatchableBranchPtrWithPatch): + (JSC::MacroAssemblerMIPS::revertJumpReplacementToPatchableBranchPtrWithPatch): + +2012-11-14 Peter Gal <galpeter@inf.u-szeged.hu> + + Fix the [-Wreturn-type] warning in JavaScriptCore/assembler/MacroAssemblerARM.h + https://bugs.webkit.org/show_bug.cgi?id=102206 + + Reviewed by Csaba Osztrogonác. + + Add a return value for the function to suppress the warning. + + * assembler/MacroAssemblerARM.h: + (JSC::MacroAssemblerARM::startOfPatchableBranchPtrWithPatch): + +2012-11-14 Sheriff Bot <webkit.review.bot@gmail.com> + + Unreviewed, rolling out r134599. + http://trac.webkit.org/changeset/134599 + https://bugs.webkit.org/show_bug.cgi?id=102225 + + It broke the 32 bit EFL build (Requested by Ossy on #webkit). + + * jit/JITPropertyAccess.cpp: + * jit/JITPropertyAccess32_64.cpp: + (JSC): + (JSC::JIT::emitGenericContiguousPutByVal): + +2012-11-14 Balazs Kilvady <kilvadyb@homejinni.com> + + [Qt][ARM]REGRESSION(r133985): It broke the build + https://bugs.webkit.org/show_bug.cgi?id=101740 + + Reviewed by Csaba Osztrogonác. + + Template function body moved to fix VALUE_PROFILER disabled case. + + * jit/JITPropertyAccess.cpp: + (JSC): + (JSC::JIT::emitGenericContiguousPutByVal): + * jit/JITPropertyAccess32_64.cpp: + +2012-11-13 Filip Pizlo <fpizlo@apple.com> + + DFG CreateThis should be able to statically account for the structure of the object it creates, if profiling indicates that this structure is always the same + https://bugs.webkit.org/show_bug.cgi?id=102017 + + Reviewed by Geoffrey Garen. + + This adds a watchpoint in JSFunction on the cached inheritor ID. It also changes + NewObject to take a structure as an operand (previously it implicitly used the owning + global object's empty object structure). Any GetCallee where the callee is predictable + is turned into a CheckFunction + WeakJSConstant, and any CreateThis on a WeakJSConstant + where the inheritor ID watchpoint is still valid is turned into an InheritorIDWatchpoint + followed by a NewObject. NewObject already accounts for the structure it uses for object + creation in the CFA. + + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::execute): + * dfg/DFGByteCodeParser.cpp: + (JSC::DFG::ByteCodeParser::parseBlock): + * dfg/DFGCSEPhase.cpp: + (JSC::DFG::CSEPhase::checkFunctionElimination): + * dfg/DFGGraph.cpp: + (JSC::DFG::Graph::dump): + * dfg/DFGNode.h: + (JSC::DFG::Node::hasFunction): + (JSC::DFG::Node::function): + (JSC::DFG::Node::hasStructure): + * dfg/DFGNodeType.h: + (DFG): + * dfg/DFGOperations.cpp: + * dfg/DFGOperations.h: + * dfg/DFGPredictionPropagationPhase.cpp: + (JSC::DFG::PredictionPropagationPhase::propagate): + * dfg/DFGSpeculativeJIT.h: + (JSC::DFG::SpeculativeJIT::callOperation): + * dfg/DFGSpeculativeJIT32_64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * runtime/Executable.h: + (JSC::JSFunction::JSFunction): + * runtime/JSBoundFunction.cpp: + (JSC): + * runtime/JSFunction.cpp: + (JSC::JSFunction::JSFunction): + (JSC::JSFunction::put): + (JSC::JSFunction::defineOwnProperty): + * runtime/JSFunction.h: + (JSC::JSFunction::tryGetKnownInheritorID): + (JSFunction): + (JSC::JSFunction::addInheritorIDWatchpoint): + +2012-11-13 Filip Pizlo <fpizlo@apple.com> + + JSFunction and its descendants should be destructible + https://bugs.webkit.org/show_bug.cgi?id=102062 + + Reviewed by Mark Hahnenberg. + + This will make it easy to place an InlineWatchpointSet inside JSFunction. In the + future, we could make JSFunction non-destructible again by making a version of + WatchpointSet that is entirely GC'd, but this seems like overkill for now. + + This is performance-neutral. + + * runtime/JSBoundFunction.cpp: + (JSC::JSBoundFunction::destroy): + (JSC): + * runtime/JSBoundFunction.h: + (JSBoundFunction): + * runtime/JSFunction.cpp: + (JSC): + (JSC::JSFunction::destroy): + * runtime/JSFunction.h: + (JSFunction): + +2012-11-13 Cosmin Truta <ctruta@rim.com> + + Uninitialized fields in class JSLock + https://bugs.webkit.org/show_bug.cgi?id=101695 + + Reviewed by Mark Hahnenberg. + + Initialize JSLock::m_ownerThread and JSLock::m_lockDropDepth. + + * runtime/JSLock.cpp: + (JSC::JSLock::JSLock): + +2012-11-13 Peter Gal <galpeter@inf.u-szeged.hu> + + Fix the ARM traditional build after r134332 + https://bugs.webkit.org/show_bug.cgi?id=102044 + + Reviewed by Zoltan Herczeg. + + Added missing methods for the MacroAssemblerARM, based on the MacroAssemblerARMv7. + + * assembler/MacroAssemblerARM.h: + (JSC::MacroAssemblerARM::canJumpReplacePatchableBranchPtrWithPatch): + (MacroAssemblerARM): + (JSC::MacroAssemblerARM::startOfPatchableBranchPtrWithPatch): + (JSC::MacroAssemblerARM::revertJumpReplacementToPatchableBranchPtrWithPatch): + +2012-11-12 Filip Pizlo <fpizlo@apple.com> + + op_get_callee should have value profiling + https://bugs.webkit.org/show_bug.cgi?id=102047 + + Reviewed by Sam Weinig. + + This will allow us to detect if the callee is always the same, which is probably + the common case for a lot of constructors. + + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::CodeBlock): + * bytecode/Opcode.h: + (JSC): + (JSC::padOpcodeName): + * bytecompiler/BytecodeGenerator.cpp: + (JSC::BytecodeGenerator::BytecodeGenerator): + * jit/JITOpcodes.cpp: + (JSC::JIT::emit_op_get_callee): + * jit/JITOpcodes32_64.cpp: + (JSC::JIT::emit_op_get_callee): + * llint/LowLevelInterpreter32_64.asm: + * llint/LowLevelInterpreter64.asm: + +2012-11-12 Filip Pizlo <fpizlo@apple.com> + + The act of getting the callee during 'this' construction should be explicit in bytecode + https://bugs.webkit.org/show_bug.cgi?id=102016 + + Reviewed by Michael Saboff. + + This is mostly a rollout of http://trac.webkit.org/changeset/116673, but also includes + changes to have create_this use the result of get_callee. + + No performance or behavioral impact. This is just meant to allow us to profile + get_callee in the future. + + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::dump): + * bytecode/Opcode.h: + (JSC): + (JSC::padOpcodeName): + * bytecompiler/BytecodeGenerator.cpp: + (JSC::BytecodeGenerator::BytecodeGenerator): + * dfg/DFGByteCodeParser.cpp: + (JSC::DFG::ByteCodeParser::parseBlock): + * dfg/DFGCapabilities.h: + (JSC::DFG::canCompileOpcode): + * jit/JIT.cpp: + (JSC::JIT::privateCompileMainPass): + * jit/JIT.h: + (JIT): + * jit/JITOpcodes.cpp: + (JSC::JIT::emit_op_get_callee): + (JSC): + (JSC::JIT::emit_op_create_this): + * jit/JITOpcodes32_64.cpp: + (JSC::JIT::emit_op_get_callee): + (JSC): + (JSC::JIT::emit_op_create_this): + * llint/LLIntSlowPaths.cpp: + (JSC::LLInt::LLINT_SLOW_PATH_DECL): + * llint/LowLevelInterpreter32_64.asm: + * llint/LowLevelInterpreter64.asm: + +2012-11-12 Filip Pizlo <fpizlo@apple.com> + + Unreviewed, fix ARMv7 build. + + * assembler/MacroAssemblerARMv7.h: + (JSC::MacroAssemblerARMv7::startOfPatchableBranchPtrWithPatch): + (JSC::MacroAssemblerARMv7::revertJumpReplacementToPatchableBranchPtrWithPatch): + +2012-11-12 Filip Pizlo <fpizlo@apple.com> + + Patching of jumps to stubs should use jump replacement rather than branch destination overwrite + https://bugs.webkit.org/show_bug.cgi?id=101909 + + Reviewed by Geoffrey Garen. + + This saves a few instructions in inline cases, on those architectures where it is + easy to figure out where to put the jump replacement. Sub-1% speed-up across the + board. + + * assembler/MacroAssemblerARMv7.h: + (MacroAssemblerARMv7): + (JSC::MacroAssemblerARMv7::canJumpReplacePatchableBranchPtrWithPatch): + (JSC::MacroAssemblerARMv7::startOfPatchableBranchPtrWithPatch): + (JSC::MacroAssemblerARMv7::revertJumpReplacementToPatchableBranchPtrWithPatch): + * assembler/MacroAssemblerX86.h: + (JSC::MacroAssemblerX86::canJumpReplacePatchableBranchPtrWithPatch): + (MacroAssemblerX86): + (JSC::MacroAssemblerX86::startOfPatchableBranchPtrWithPatch): + (JSC::MacroAssemblerX86::revertJumpReplacementToPatchableBranchPtrWithPatch): + * assembler/MacroAssemblerX86_64.h: + (JSC::MacroAssemblerX86_64::canJumpReplacePatchableBranchPtrWithPatch): + (MacroAssemblerX86_64): + (JSC::MacroAssemblerX86_64::startOfPatchableBranchPtrWithPatch): + (JSC::MacroAssemblerX86_64::revertJumpReplacementToPatchableBranchPtrWithPatch): + * assembler/RepatchBuffer.h: + (JSC::RepatchBuffer::startOfPatchableBranchPtrWithPatch): + (RepatchBuffer): + (JSC::RepatchBuffer::replaceWithJump): + (JSC::RepatchBuffer::revertJumpReplacementToPatchableBranchPtrWithPatch): + * assembler/X86Assembler.h: + (X86Assembler): + (JSC::X86Assembler::revertJumpTo_movq_i64r): + (JSC::X86Assembler::revertJumpTo_cmpl_im_force32): + (X86InstructionFormatter): + * bytecode/StructureStubInfo.h: + * dfg/DFGRepatch.cpp: + (JSC::DFG::replaceWithJump): + (DFG): + (JSC::DFG::tryCacheGetByID): + (JSC::DFG::tryBuildGetByIDList): + (JSC::DFG::tryBuildGetByIDProtoList): + (JSC::DFG::tryCachePutByID): + (JSC::DFG::dfgResetGetByID): + (JSC::DFG::dfgResetPutByID): + +2012-11-11 Filip Pizlo <fpizlo@apple.com> + + DFG ArithMul overflow check elimination is too aggressive + https://bugs.webkit.org/show_bug.cgi?id=101871 + + Reviewed by Oliver Hunt. + + The code was ignoring the fact that ((a * b) | 0) == (((a | 0) * (b | 0)) | 0) + only holds if a * b < 2^53. So, I changed it to only enable the optimization + when a < 2^22 and b is an int32 (and vice versa), using a super trivial peephole + analysis to prove the inequality. I considered writing an epic forward flow + formulation that tracks the ranges of integer values but then I thought better + of it. + + This also rewires the ArithMul integer speculation logic. Previously, we would + assume that an ArithMul was only UsedAsNumber if it escaped, and separately we + would decide whether to speculate integer based on a proof of the <2^22 + inequality. Now, we treat the double rounding behavior of ArithMul as if the + result was UsedAsNumber even if it did not escape. Then we try to prove that + double rounding cannot happen by attemping to prove that a < 2^22. This then + feeds back into the decision of whether or not to speculate integer (if we fail + to prove a < 2^22 then we're UsedAsNumber, and if we're also MayOverflow then + that forces double speculation). + + No performance impact. It just fixes a bug. + + * dfg/DFGGraph.h: + (JSC::DFG::Graph::mulShouldSpeculateInteger): + * dfg/DFGPredictionPropagationPhase.cpp: + (PredictionPropagationPhase): + (JSC::DFG::PredictionPropagationPhase::isWithinPowerOfTwoForConstant): + (JSC::DFG::PredictionPropagationPhase::isWithinPowerOfTwoNonRecursive): + (JSC::DFG::PredictionPropagationPhase::isWithinPowerOfTwo): + (JSC::DFG::PredictionPropagationPhase::propagate): + +2012-11-11 Filip Pizlo <fpizlo@apple.com> + + DFG should not emit function checks if we've already proved that the operand is that exact function + https://bugs.webkit.org/show_bug.cgi?id=101885 + + Reviewed by Oliver Hunt. + + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::execute): + * dfg/DFGAbstractValue.h: + (JSC::DFG::AbstractValue::filterByValue): + (AbstractValue): + * dfg/DFGConstantFoldingPhase.cpp: + (JSC::DFG::ConstantFoldingPhase::foldConstants): + +2012-11-12 Kentaro Hara <haraken@chromium.org> + + [V8][JSC] ScriptProfileNode::callUID needs not to be [Custom] + https://bugs.webkit.org/show_bug.cgi?id=101892 + + Reviewed by Adam Barth. + + Added callUID(), which enables us to kill custom bindings for ScriptProfileNode::callUID. + + * profiler/ProfileNode.h: + (JSC::ProfileNode::callUID): + +2012-11-12 Carlos Garcia Campos <cgarcia@igalia.com> + + Unreviewed. Fix make distcheck. + + * GNUmakefile.list.am: Add missing header. + +2012-11-11 Michael Pruett <michael@68k.org> + + Fix assertion failure in JSObject::tryGetIndexQuickly() + https://bugs.webkit.org/show_bug.cgi?id=101869 + + Reviewed by Filip Pizlo. + + Currently JSObject::tryGetIndexQuickly() triggers an assertion + failure when the object has an undecided indexing type. This + case should be treated the same as a blank indexing type. + + * runtime/JSObject.h: + (JSC::JSObject::tryGetIndexQuickly): + +2012-11-11 Filip Pizlo <fpizlo@apple.com> + + DFG register allocation should be greedy rather than round-robin + https://bugs.webkit.org/show_bug.cgi?id=101870 + + Reviewed by Geoffrey Garen. + + This simplifies the code, reduces some code duplication, and shows some slight + performance improvements in a few places, likely due to the fact that lower-numered + registers also typically have smaller encodings. + + * dfg/DFGRegisterBank.h: + (JSC::DFG::RegisterBank::RegisterBank): + (JSC::DFG::RegisterBank::tryAllocate): + (JSC::DFG::RegisterBank::allocate): + (JSC::DFG::RegisterBank::allocateInternal): + (RegisterBank): + +2012-11-11 Kenichi Ishibashi <bashi@chromium.org> + + WTFString::utf8() should have a mode of conversion to use replacement character + https://bugs.webkit.org/show_bug.cgi?id=101678 + + Reviewed by Alexey Proskuryakov. + + Follow the change on String::utf8() + + * runtime/JSGlobalObjectFunctions.cpp: + (JSC::encode): Pass String::StrictConversion instead of true to String::utf8(). + +2012-11-10 Filip Pizlo <fpizlo@apple.com> + + DFG should optimize out the NaN check on loads from double arrays if the array prototype chain is having a great time + https://bugs.webkit.org/show_bug.cgi?id=101718 + + Reviewed by Geoffrey Garen. + + If we're reading from a JSArray in double mode, where the array's structure is + primordial (all aspects of the structure are unchanged except for indexing type), + and the result of the load is used in arithmetic that is known to not distinguish + between NaN and undefined, then we should not emit a NaN check. Looks like a 5% + win on navier-stokes. + + Also fixed an OpInfo initialization goof for String ops that was revealed by this + change. + + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::execute): + * dfg/DFGArrayMode.cpp: + (JSC::DFG::arraySpeculationToString): + * dfg/DFGArrayMode.h: + (JSC::DFG::ArrayMode::isSaneChain): + (ArrayMode): + (JSC::DFG::ArrayMode::isInBounds): + * dfg/DFGByteCodeParser.cpp: + (JSC::DFG::ByteCodeParser::handleIntrinsic): + * dfg/DFGFixupPhase.cpp: + (JSC::DFG::FixupPhase::fixupNode): + * dfg/DFGNodeFlags.cpp: + (JSC::DFG::nodeFlagsAsString): + * dfg/DFGNodeFlags.h: + (DFG): + * dfg/DFGPredictionPropagationPhase.cpp: + (JSC::DFG::PredictionPropagationPhase::propagate): + * dfg/DFGSpeculativeJIT32_64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * runtime/JSGlobalObject.cpp: + (JSC::JSGlobalObject::arrayPrototypeChainIsSane): + (JSC): + * runtime/JSGlobalObject.h: + (JSGlobalObject): + +2012-11-10 Filip Pizlo <fpizlo@apple.com> + + DFG constant folding and CFG simplification should be smart enough to know that if a logical op's operand is proven to have a non-masquerading structure then it always evaluates to true + https://bugs.webkit.org/show_bug.cgi?id=101511 + + Reviewed by Geoffrey Garen. + + This is the second attempt at this patch, which fixes the !"" case. + + To make life easier, this moves BranchDirection into BasicBlock so that after + running the CFA, we always know, for each block, what direction the CFA + proved. CFG simplification now both uses and preserves cfaBranchDirection in + its transformations. + + Also made both LogicalNot and Branch check whether the operand is a known cell + with a known structure, and if so, made them do the appropriate folding. + + 5% speed-up on V8/raytrace because it makes raytrace's own null checks + evaporate (i.e. idioms like 'if (!x) throw "unhappiness"') thanks to the fact + that we were already doing structure check hoisting. + + * JavaScriptCore.xcodeproj/project.pbxproj: + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::endBasicBlock): + (JSC::DFG::AbstractState::execute): + (JSC::DFG::AbstractState::mergeToSuccessors): + * dfg/DFGAbstractState.h: + (AbstractState): + * dfg/DFGBasicBlock.h: + (JSC::DFG::BasicBlock::BasicBlock): + (BasicBlock): + * dfg/DFGBranchDirection.h: Added. + (DFG): + (JSC::DFG::branchDirectionToString): + (JSC::DFG::isKnownDirection): + (JSC::DFG::branchCondition): + * dfg/DFGCFGSimplificationPhase.cpp: + (JSC::DFG::CFGSimplificationPhase::run): + (JSC::DFG::CFGSimplificationPhase::mergeBlocks): + +2012-11-10 Sheriff Bot <webkit.review.bot@gmail.com> + + Unreviewed, rolling out r133971. + http://trac.webkit.org/changeset/133971 + https://bugs.webkit.org/show_bug.cgi?id=101839 + + Causes WebProcess to hang at 100% on www.apple.com (Requested + by kling on #webkit). + + * JavaScriptCore.xcodeproj/project.pbxproj: + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::endBasicBlock): + (JSC::DFG::AbstractState::execute): + (JSC::DFG::AbstractState::mergeToSuccessors): + * dfg/DFGAbstractState.h: + (JSC::DFG::AbstractState::branchDirectionToString): + (AbstractState): + * dfg/DFGBasicBlock.h: + (JSC::DFG::BasicBlock::BasicBlock): + (BasicBlock): + * dfg/DFGBranchDirection.h: Removed. + * dfg/DFGCFGSimplificationPhase.cpp: + (JSC::DFG::CFGSimplificationPhase::run): + (JSC::DFG::CFGSimplificationPhase::mergeBlocks): + +2012-11-09 Filip Pizlo <fpizlo@apple.com> + + If the DFG ArrayMode says that an access is on an OriginalArray, then the checks should always enforce this + https://bugs.webkit.org/show_bug.cgi?id=101720 + + Reviewed by Mark Hahnenberg. + + Previously, "original" arrays was just a hint that we could find the structure + of the array if we needed to even if the array profile didn't have it due to + polymorphism. Now, "original" arrays are a property that is actually checked: + if an array access has ArrayMode::arrayClass() == Array::OriginalArray, then we + can be sure that the code performing the access is dealing with not just a + JSArray, but a JSArray that has no named properties, no indexed accessors, and + the ArrayPrototype as its prototype. This will be useful for optimizations that + are being done as part of https://bugs.webkit.org/show_bug.cgi?id=101720. + + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::execute): + * dfg/DFGArrayMode.cpp: + (JSC::DFG::ArrayMode::originalArrayStructure): + (DFG): + (JSC::DFG::ArrayMode::alreadyChecked): + * dfg/DFGArrayMode.h: + (JSC): + (DFG): + (JSC::DFG::ArrayMode::withProfile): + (ArrayMode): + (JSC::DFG::ArrayMode::benefitsFromOriginalArray): + * dfg/DFGConstantFoldingPhase.cpp: + (JSC::DFG::ConstantFoldingPhase::foldConstants): + * dfg/DFGFixupPhase.cpp: + (JSC::DFG::FixupPhase::checkArray): + * dfg/DFGSpeculativeJIT.cpp: + (JSC::DFG::SpeculativeJIT::jumpSlowForUnwantedArrayMode): + (JSC::DFG::SpeculativeJIT::checkArray): + (JSC::DFG::SpeculativeJIT::compileGetByValOnString): + (JSC::DFG::SpeculativeJIT::compileGetByValOnIntTypedArray): + (JSC::DFG::SpeculativeJIT::compileGetByValOnFloatTypedArray): + (JSC::DFG::SpeculativeJIT::compilePutByValForFloatTypedArray): + (JSC::DFG::SpeculativeJIT::compileGetByValOnArguments): + (JSC::DFG::SpeculativeJIT::compileGetArgumentsLength): + +2012-11-09 Filip Pizlo <fpizlo@apple.com> + + Fix indentation of BooleanPrototype.h + + Rubber stamped by Mark Hahnenberg. + + * runtime/BooleanPrototype.h: + +2012-11-09 Filip Pizlo <fpizlo@apple.com> + + Fix indentation of BooleanObject.h + + Rubber stamped by Mark Hahnenberg. + + * runtime/BooleanObject.h: + +2012-11-09 Filip Pizlo <fpizlo@apple.com> + + Fix indentation of BooleanConstructor.h + + Rubber stamped by Mark Hahnenberg. + + * runtime/BooleanConstructor.h: + +2012-11-09 Filip Pizlo <fpizlo@apple.com> + + Fix indentation of BatchedTransitionOptimizer.h + + Rubber stamped by Mark Hahnenberg. + + * runtime/BatchedTransitionOptimizer.h: + +2012-11-09 Oliver Hunt <oliver@apple.com> + + So Thingy probably isn't the best name for a class, so + renamed to CacheMap. + + RS=Geoff + + * runtime/CodeCache.h: + (JSC::CacheMap::CacheMap): + +2012-11-09 Filip Pizlo <fpizlo@apple.com> + + ArrayPrototype should start out with a blank indexing type + https://bugs.webkit.org/show_bug.cgi?id=101719 + + Reviewed by Mark Hahnenberg. + + This allows us to track if the array prototype ever ends up with indexed + properties. + + * runtime/ArrayPrototype.cpp: + (JSC::ArrayPrototype::create): + (JSC::ArrayPrototype::ArrayPrototype): + * runtime/ArrayPrototype.h: + (ArrayPrototype): + (JSC::ArrayPrototype::createStructure): + +2012-11-08 Mark Hahnenberg <mhahnenberg@apple.com> + + MarkStackArray should use the BlockAllocator instead of the MarkStackSegmentAllocator + https://bugs.webkit.org/show_bug.cgi?id=101642 + + Reviewed by Filip Pizlo. + + MarkStackSegmentAllocator is like a miniature version of the BlockAllocator. Now that the BlockAllocator has support + for a variety of block sizes, we should get rid of the MarkStackSegmentAllocator in favor of the BlockAllocator. + + * heap/BlockAllocator.h: Add new specializations of regionSetFor for the new MarkStackSegments. + (JSC): + (JSC::MarkStackSegment): + * heap/GCThreadSharedData.cpp: + (JSC::GCThreadSharedData::GCThreadSharedData): + (JSC::GCThreadSharedData::reset): + * heap/GCThreadSharedData.h: + (GCThreadSharedData): + * heap/MarkStack.cpp: + (JSC::MarkStackArray::MarkStackArray): We now have a doubly linked list of MarkStackSegments, so we need to refactor + all the places that used the old custom tail/previous logic. + (JSC::MarkStackArray::~MarkStackArray): + (JSC::MarkStackArray::expand): + (JSC::MarkStackArray::refill): + (JSC::MarkStackArray::donateSomeCellsTo): Refactor to use the new linked list. + (JSC::MarkStackArray::stealSomeCellsFrom): Ditto. + * heap/MarkStack.h: + (JSC): + (MarkStackSegment): + (JSC::MarkStackSegment::MarkStackSegment): + (JSC::MarkStackSegment::sizeFromCapacity): + (MarkStackArray): + * heap/MarkStackInlines.h: + (JSC::MarkStackSegment::create): + (JSC): + (JSC::MarkStackArray::postIncTop): + (JSC::MarkStackArray::preDecTop): + (JSC::MarkStackArray::setTopForFullSegment): + (JSC::MarkStackArray::setTopForEmptySegment): + (JSC::MarkStackArray::top): + (JSC::MarkStackArray::validatePrevious): + (JSC::MarkStackArray::append): + (JSC::MarkStackArray::removeLast): + (JSC::MarkStackArray::isEmpty): + (JSC::MarkStackArray::size): + * heap/SlotVisitor.cpp: + (JSC::SlotVisitor::SlotVisitor): + +2012-11-09 Gabor Ballabas <gaborb@inf.u-szeged.hu> + + [Qt] r133953 broke the ARM_TRADITIONAL build + https://bugs.webkit.org/show_bug.cgi?id=101706 + + Reviewed by Csaba Osztrogonác. + + Fix for both hardfp and softfp. + + * dfg/DFGCCallHelpers.h: + (CCallHelpers): + (JSC::DFG::CCallHelpers::setupArgumentsWithExecState): + +2012-11-09 Sheriff Bot <webkit.review.bot@gmail.com> + + Unreviewed, rolling out r134051. + http://trac.webkit.org/changeset/134051 + https://bugs.webkit.org/show_bug.cgi?id=101757 + + It didn't fix the build (Requested by Ossy on #webkit). + + * dfg/DFGCCallHelpers.h: + (JSC::DFG::CCallHelpers::setupArgumentsWithExecState): + +2012-11-09 Gabor Ballabas <gaborb@inf.u-szeged.hu> + + [Qt] r133953 broke the ARM_TRADITIONAL build + https://bugs.webkit.org/show_bug.cgi?id=101706 + + Reviewed by Csaba Osztrogonác. + + Fix the ARM_TRADITIONAL build after r133953 + + * dfg/DFGCCallHelpers.h: + (JSC::DFG::CCallHelpers::setupArgumentsWithExecState): + (CCallHelpers): + 2012-11-09 Csaba Osztrogonác <ossy@webkit.org> [Qt] Fix the LLINT build from ARMv7 platform @@ -10,6 +1576,650 @@ * DerivedSources.pri: * JavaScriptCore.pro: +2012-11-08 Filip Pizlo <fpizlo@apple.com> + + ArrayPrototype.h should have correct indentation + + Rubber stamped by Sam Weinig. + + * runtime/ArrayPrototype.h: + +2012-11-08 Mark Lam <mark.lam@apple.com> + + Renamed ...InlineMethods.h files to ...Inlines.h. + https://bugs.webkit.org/show_bug.cgi?id=101145. + + Reviewed by Geoffrey Garen. + + This is only a refactoring effort to rename the files. There are no + functionality changes. + + * API/JSObjectRef.cpp: + * GNUmakefile.list.am: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: + * JavaScriptCore.xcodeproj/project.pbxproj: + * bytecode/CodeBlock.cpp: + * dfg/DFGOperations.cpp: + * heap/ConservativeRoots.cpp: + * heap/CopiedBlock.h: + * heap/CopiedSpace.cpp: + * heap/CopiedSpaceInlineMethods.h: Removed. + * heap/CopiedSpaceInlines.h: Copied from Source/JavaScriptCore/heap/CopiedSpaceInlineMethods.h. + * heap/CopyVisitor.cpp: + * heap/CopyVisitorInlineMethods.h: Removed. + * heap/CopyVisitorInlines.h: Copied from Source/JavaScriptCore/heap/CopyVisitorInlineMethods.h. + * heap/GCThread.cpp: + * heap/GCThreadSharedData.cpp: + * heap/HandleStack.cpp: + * heap/Heap.cpp: + * heap/HeapRootVisitor.h: + * heap/MarkStack.cpp: + * heap/MarkStackInlineMethods.h: Removed. + * heap/MarkStackInlines.h: Copied from Source/JavaScriptCore/heap/MarkStackInlineMethods.h. + * heap/SlotVisitor.cpp: + * heap/SlotVisitor.h: + * heap/SlotVisitorInlineMethods.h: Removed. + * heap/SlotVisitorInlines.h: Copied from Source/JavaScriptCore/heap/SlotVisitorInlineMethods.h. + * jit/HostCallReturnValue.cpp: + * jit/JIT.cpp: + * jit/JITArithmetic.cpp: + * jit/JITArithmetic32_64.cpp: + * jit/JITCall.cpp: + * jit/JITCall32_64.cpp: + * jit/JITInlineMethods.h: Removed. + * jit/JITInlines.h: Copied from Source/JavaScriptCore/jit/JITInlineMethods.h. + * jit/JITOpcodes.cpp: + * jit/JITOpcodes32_64.cpp: + * jit/JITPropertyAccess.cpp: + * jit/JITPropertyAccess32_64.cpp: + * jsc.cpp: + * runtime/ArrayConstructor.cpp: + * runtime/ArrayPrototype.cpp: + * runtime/ButterflyInlineMethods.h: Removed. + * runtime/ButterflyInlines.h: Copied from Source/JavaScriptCore/runtime/ButterflyInlineMethods.h. + * runtime/IndexingHeaderInlineMethods.h: Removed. + * runtime/IndexingHeaderInlines.h: Copied from Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h. + * runtime/JSActivation.h: + * runtime/JSArray.cpp: + * runtime/JSArray.h: + * runtime/JSCell.h: + * runtime/JSObject.cpp: + * runtime/JSValueInlineMethods.h: Removed. + * runtime/JSValueInlines.h: Copied from Source/JavaScriptCore/runtime/JSValueInlineMethods.h. + * runtime/LiteralParser.cpp: + * runtime/ObjectConstructor.cpp: + * runtime/Operations.h: + * runtime/RegExpMatchesArray.cpp: + * runtime/RegExpObject.cpp: + * runtime/StringPrototype.cpp: + +2012-11-08 Filip Pizlo <fpizlo@apple.com> + + ArrayConstructor.h should have correct indentation + + Rubber stamped by Sam Weinig. + + * runtime/ArrayConstructor.h: + +2012-11-08 Filip Pizlo <fpizlo@apple.com> + + DFG should know that int == null is always false + https://bugs.webkit.org/show_bug.cgi?id=101665 + + Reviewed by Oliver Hunt. + + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::execute): + +2012-11-08 Filip Pizlo <fpizlo@apple.com> + + Arguments.h should have correct indentation + + Rubber stamped by Sam Weinig. + + * runtime/Arguments.h: + +2012-11-08 Filip Pizlo <fpizlo@apple.com> + + It should be possible to JIT compile get_by_vals and put_by_vals even if the DFG is disabled. + + Reviewed by Oliver Hunt. + + * jit/JITInlineMethods.h: + (JSC::JIT::chooseArrayMode): + +2012-11-08 Filip Pizlo <fpizlo@apple.com> + + op_call should have LLInt call link info even if the DFG is disabled + https://bugs.webkit.org/show_bug.cgi?id=101672 + + Reviewed by Oliver Hunt. + + Get rid of the evil uses of fall-through. + + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::CodeBlock): + +2012-11-08 Oliver Hunt <oliver@apple.com> + + Improve effectiveness of function-level caching + https://bugs.webkit.org/show_bug.cgi?id=101667 + + Reviewed by Filip Pizlo. + + Added a random-eviction based cache for unlinked functions, and switch + UnlinkedFunctionExecutable's code references to Weak<>, thereby letting + us remove the explicit UnlinkedFunctionExecutable::clearCode() calls that + were being triggered by GC. + + Refactored the random eviction part of the CodeCache into a separate data + structure so that I didn't have to duplicate the code again, and then used + that for the new function cache. + + * bytecode/UnlinkedCodeBlock.cpp: + (JSC::UnlinkedFunctionExecutable::visitChildren): + (JSC::UnlinkedFunctionExecutable::codeBlockFor): + * bytecode/UnlinkedCodeBlock.h: + (JSC::UnlinkedFunctionExecutable::clearCodeForRecompilation): + (UnlinkedFunctionExecutable): + * debugger/Debugger.cpp: + * runtime/CodeCache.cpp: + (JSC::CodeCache::getCodeBlock): + (JSC::CodeCache::generateFunctionCodeBlock): + (JSC::CodeCache::getFunctionExecutableFromGlobalCode): + (JSC::CodeCache::usedFunctionCode): + (JSC): + * runtime/Executable.cpp: + (JSC::FunctionExecutable::clearUnlinkedCodeForRecompilationIfNotCompiling): + (JSC::FunctionExecutable::clearCode): + * runtime/Executable.h: + (FunctionExecutable): + +2012-11-07 Filip Pizlo <fpizlo@apple.com> + + DFG constant folding and CFG simplification should be smart enough to know that if a logical op's operand is proven to have a non-masquerading structure then it always evaluates to true + https://bugs.webkit.org/show_bug.cgi?id=101511 + + Reviewed by Oliver Hunt. + + To make life easier, this moves BranchDirection into BasicBlock so that after + running the CFA, we always know, for each block, what direction the CFA + proved. CFG simplification now both uses and preserves cfaBranchDirection in + its transformations. + + Also made both LogicalNot and Branch check whether the operand is a known cell + with a known structure, and if so, made them do the appropriate folding. + + 5% speed-up on V8/raytrace because it makes raytrace's own null checks + evaporate (i.e. idioms like 'if (!x) throw "unhappiness"') thanks to the fact + that we were already doing structure check hoisting. + + * JavaScriptCore.xcodeproj/project.pbxproj: + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::endBasicBlock): + (JSC::DFG::AbstractState::execute): + (JSC::DFG::AbstractState::mergeToSuccessors): + * dfg/DFGAbstractState.h: + (AbstractState): + * dfg/DFGBasicBlock.h: + (JSC::DFG::BasicBlock::BasicBlock): + (BasicBlock): + * dfg/DFGBranchDirection.h: Added. + (DFG): + (JSC::DFG::branchDirectionToString): + (JSC::DFG::isKnownDirection): + (JSC::DFG::branchCondition): + * dfg/DFGCFGSimplificationPhase.cpp: + (JSC::DFG::CFGSimplificationPhase::run): + (JSC::DFG::CFGSimplificationPhase::mergeBlocks): + +2012-11-08 Christophe Dumez <christophe.dumez@intel.com> + + [JSC] HTML extensions to String.prototype should escape " as " in argument values + https://bugs.webkit.org/show_bug.cgi?id=90667 + + Reviewed by Benjamin Poulain. + + Escape quotation mark as " in argument values to: + - String.prototype.anchor(name) + - String.prototype.fontcolor(color) + - String.prototype.fontsize(size) + - String.prototype.link(href) + + This behavior matches Chromium/V8 and Firefox/Spidermonkey + implementations and is requited by: + http://mathias.html5.org/specs/javascript/#escapeattributevalue + + This also fixes a potential security risk (XSS vector). + + * runtime/StringPrototype.cpp: + (JSC::stringProtoFuncFontcolor): + (JSC::stringProtoFuncFontsize): + (JSC::stringProtoFuncAnchor): + (JSC::stringProtoFuncLink): + +2012-11-08 Anders Carlsson <andersca@apple.com> + + HeapStatistics::s_pauseTimeStarts and s_pauseTimeEnds should be Vectors + https://bugs.webkit.org/show_bug.cgi?id=101651 + + Reviewed by Andreas Kling. + + HeapStatistics uses Deques when Vectors would work just as good. + + * heap/HeapStatistics.cpp: + * heap/HeapStatistics.h: + (HeapStatistics): + +2012-11-07 Filip Pizlo <fpizlo@apple.com> + + DFG should not assume that something is a double just because it might be undefined + https://bugs.webkit.org/show_bug.cgi?id=101438 + + Reviewed by Oliver Hunt. + + This changes all non-bitop arithmetic to (a) statically expect that variables are + defined prior to use in arithmetic and (b) not fall off into double paths just + because a value may not be a number. This is accomplished with two new notions of + speculation: + + shouldSpeculateIntegerExpectingDefined: Should we speculate that the value is an + integer if we ignore undefined (i.e. SpecOther) predictions? + + shouldSpeculateIntegerForArithmetic: Should we speculate that the value is an + integer if we ignore non-numeric predictions? + + This is a ~2x speed-up on programs that seem to our prediction propagator to have + paths in which otherwise numeric variables are undefined. + + * bytecode/SpeculatedType.h: + (JSC::isInt32SpeculationForArithmetic): + (JSC): + (JSC::isInt32SpeculationExpectingDefined): + (JSC::isDoubleSpeculationForArithmetic): + (JSC::isNumberSpeculationExpectingDefined): + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::execute): + * dfg/DFGFixupPhase.cpp: + (JSC::DFG::FixupPhase::fixupNode): + * dfg/DFGGraph.h: + (JSC::DFG::Graph::addShouldSpeculateInteger): + (JSC::DFG::Graph::mulShouldSpeculateInteger): + (JSC::DFG::Graph::negateShouldSpeculateInteger): + (JSC::DFG::Graph::addImmediateShouldSpeculateInteger): + (JSC::DFG::Graph::mulImmediateShouldSpeculateInteger): + * dfg/DFGNode.h: + (JSC::DFG::Node::shouldSpeculateIntegerForArithmetic): + (Node): + (JSC::DFG::Node::shouldSpeculateIntegerExpectingDefined): + (JSC::DFG::Node::shouldSpeculateDoubleForArithmetic): + (JSC::DFG::Node::shouldSpeculateNumberExpectingDefined): + * dfg/DFGPredictionPropagationPhase.cpp: + (JSC::DFG::PredictionPropagationPhase::propagate): + (JSC::DFG::PredictionPropagationPhase::doRoundOfDoubleVoting): + * dfg/DFGSpeculativeJIT.cpp: + (JSC::DFG::SpeculativeJIT::compileAdd): + (JSC::DFG::SpeculativeJIT::compileArithMod): + * dfg/DFGSpeculativeJIT32_64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * jit/JITArithmetic.cpp: + (JSC::JIT::emit_op_div): + +2012-11-06 Filip Pizlo <fpizlo@apple.com> + + JSC should infer when indexed storage contains only integers or doubles + https://bugs.webkit.org/show_bug.cgi?id=98606 + + Reviewed by Oliver Hunt. + + This adds two new indexing types: int32 and double. It also adds array allocation profiling, + which allows array allocations to converge to allocating arrays using those types to which + those arrays would have been converted. + + 20% speed-up on navier-stokes. 40% speed-up on various Kraken DSP tests. Some slow-downs too, + but a performance win overall on all benchmarks we track. + + * API/JSObjectRef.cpp: + (JSObjectMakeArray): + * CMakeLists.txt: + * GNUmakefile.list.am: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: + * JavaScriptCore.xcodeproj/project.pbxproj: + * Target.pri: + * assembler/AbstractMacroAssembler.h: + (JumpList): + (JSC::AbstractMacroAssembler::JumpList::JumpList): + * assembler/MacroAssemblerX86Common.h: + (JSC::MacroAssemblerX86Common::branchDouble): + * assembler/X86Assembler.h: + (JSC::X86Assembler::jnp): + (X86Assembler): + (JSC::X86Assembler::X86InstructionFormatter::emitRex): + * bytecode/ArrayAllocationProfile.cpp: Added. + (JSC): + (JSC::ArrayAllocationProfile::updateIndexingType): + * bytecode/ArrayAllocationProfile.h: Added. + (JSC): + (ArrayAllocationProfile): + (JSC::ArrayAllocationProfile::ArrayAllocationProfile): + (JSC::ArrayAllocationProfile::selectIndexingType): + (JSC::ArrayAllocationProfile::updateLastAllocation): + (JSC::ArrayAllocationProfile::selectIndexingTypeFor): + (JSC::ArrayAllocationProfile::updateLastAllocationFor): + * bytecode/ArrayProfile.cpp: + (JSC::ArrayProfile::updatedObservedArrayModes): + (JSC): + * bytecode/ArrayProfile.h: + (JSC): + (JSC::arrayModesInclude): + (JSC::shouldUseSlowPutArrayStorage): + (JSC::shouldUseFastArrayStorage): + (JSC::shouldUseContiguous): + (JSC::shouldUseDouble): + (JSC::shouldUseInt32): + (ArrayProfile): + * bytecode/ByValInfo.h: + (JSC::isOptimizableIndexingType): + (JSC::jitArrayModeForIndexingType): + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::dump): + (JSC::CodeBlock::CodeBlock): + (JSC::CodeBlock::updateAllPredictionsAndCountLiveness): + (JSC): + (JSC::CodeBlock::updateAllValueProfilePredictions): + (JSC::CodeBlock::updateAllArrayPredictions): + (JSC::CodeBlock::updateAllPredictions): + (JSC::CodeBlock::shouldOptimizeNow): + * bytecode/CodeBlock.h: + (CodeBlock): + (JSC::CodeBlock::numberOfArrayAllocationProfiles): + (JSC::CodeBlock::addArrayAllocationProfile): + (JSC::CodeBlock::updateAllValueProfilePredictions): + (JSC::CodeBlock::updateAllArrayPredictions): + * bytecode/DFGExitProfile.h: + (JSC::DFG::exitKindToString): + * bytecode/Instruction.h: + (JSC): + (JSC::Instruction::Instruction): + * bytecode/Opcode.h: + (JSC): + (JSC::padOpcodeName): + * bytecode/SpeculatedType.h: + (JSC): + (JSC::isRealNumberSpeculation): + * bytecode/UnlinkedCodeBlock.cpp: + (JSC::UnlinkedCodeBlock::UnlinkedCodeBlock): + * bytecode/UnlinkedCodeBlock.h: + (JSC): + (JSC::UnlinkedCodeBlock::addArrayAllocationProfile): + (JSC::UnlinkedCodeBlock::numberOfArrayAllocationProfiles): + (UnlinkedCodeBlock): + * bytecompiler/BytecodeGenerator.cpp: + (JSC::BytecodeGenerator::newArrayAllocationProfile): + (JSC): + (JSC::BytecodeGenerator::emitNewArray): + (JSC::BytecodeGenerator::emitExpectedFunctionSnippet): + * bytecompiler/BytecodeGenerator.h: + (BytecodeGenerator): + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::execute): + * dfg/DFGArrayMode.cpp: + (JSC::DFG::ArrayMode::fromObserved): + (JSC::DFG::ArrayMode::refine): + (DFG): + (JSC::DFG::ArrayMode::alreadyChecked): + (JSC::DFG::arrayTypeToString): + * dfg/DFGArrayMode.h: + (JSC::DFG::ArrayMode::withType): + (ArrayMode): + (JSC::DFG::ArrayMode::withTypeAndConversion): + (JSC::DFG::ArrayMode::usesButterfly): + (JSC::DFG::ArrayMode::isSpecific): + (JSC::DFG::ArrayMode::supportsLength): + (JSC::DFG::ArrayMode::arrayModesThatPassFiltering): + * dfg/DFGByteCodeParser.cpp: + (JSC::DFG::ByteCodeParser::getArrayMode): + (ByteCodeParser): + (JSC::DFG::ByteCodeParser::handleIntrinsic): + (JSC::DFG::ByteCodeParser::handleConstantInternalFunction): + (JSC::DFG::ByteCodeParser::parseBlock): + * dfg/DFGCCallHelpers.h: + (JSC::DFG::CCallHelpers::setupArgumentsWithExecState): + (CCallHelpers): + * dfg/DFGCallArrayAllocatorSlowPathGenerator.h: + (JSC::DFG::CallArrayAllocatorSlowPathGenerator::generateInternal): + (JSC::DFG::CallArrayAllocatorWithVariableSizeSlowPathGenerator::generateInternal): + * dfg/DFGFixupPhase.cpp: + (JSC::DFG::FixupPhase::fixupNode): + (JSC::DFG::FixupPhase::checkArray): + * dfg/DFGGraph.cpp: + (JSC::DFG::Graph::dump): + * dfg/DFGGraph.h: + (JSC::DFG::Graph::byValIsPure): + * dfg/DFGNode.h: + (NewArrayBufferData): + (JSC::DFG::Node::hasIndexingType): + (Node): + (JSC::DFG::Node::indexingType): + (JSC::DFG::Node::setIndexingType): + * dfg/DFGOperations.cpp: + * dfg/DFGOperations.h: + * dfg/DFGPredictionPropagationPhase.cpp: + (JSC::DFG::PredictionPropagationPhase::doRoundOfDoubleVoting): + * dfg/DFGSpeculativeJIT.cpp: + (JSC::DFG::SpeculativeJIT::emitAllocateJSArray): + (JSC::DFG::SpeculativeJIT::jumpSlowForUnwantedArrayMode): + (DFG): + (JSC::DFG::SpeculativeJIT::checkArray): + (JSC::DFG::SpeculativeJIT::arrayify): + (JSC::DFG::SpeculativeJIT::compileDoublePutByVal): + (JSC::DFG::SpeculativeJIT::compileGetArrayLength): + * dfg/DFGSpeculativeJIT.h: + (JSC::DFG::SpeculativeJIT::callOperation): + (SpeculativeJIT): + (SpeculateIntegerOperand): + (JSC::DFG::SpeculateIntegerOperand::use): + (SpeculateDoubleOperand): + (JSC::DFG::SpeculateDoubleOperand::use): + * dfg/DFGSpeculativeJIT32_64.cpp: + (DFG): + (JSC::DFG::SpeculativeJIT::compileContiguousPutByVal): + (JSC::DFG::SpeculativeJIT::compile): + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * jit/JIT.h: + (JSC::JIT::emitInt32GetByVal): + (JIT): + (JSC::JIT::emitInt32PutByVal): + (JSC::JIT::emitDoublePutByVal): + (JSC::JIT::emitContiguousPutByVal): + * jit/JITExceptions.cpp: + (JSC::genericThrow): + * jit/JITInlineMethods.h: + (JSC::arrayProfileSaw): + (JSC::JIT::chooseArrayMode): + * jit/JITOpcodes.cpp: + (JSC::JIT::emit_op_new_array): + (JSC::JIT::emit_op_new_array_with_size): + (JSC::JIT::emit_op_new_array_buffer): + * jit/JITPropertyAccess.cpp: + (JSC::JIT::emit_op_get_by_val): + (JSC::JIT::emitDoubleGetByVal): + (JSC): + (JSC::JIT::emitContiguousGetByVal): + (JSC::JIT::emit_op_put_by_val): + (JSC::JIT::emitGenericContiguousPutByVal): + (JSC::JIT::emitSlow_op_put_by_val): + (JSC::JIT::privateCompileGetByVal): + (JSC::JIT::privateCompilePutByVal): + * jit/JITPropertyAccess32_64.cpp: + (JSC::JIT::emit_op_get_by_val): + (JSC::JIT::emitContiguousGetByVal): + (JSC::JIT::emitDoubleGetByVal): + (JSC): + (JSC::JIT::emit_op_put_by_val): + (JSC::JIT::emitGenericContiguousPutByVal): + (JSC::JIT::emitSlow_op_put_by_val): + * jit/JITStubs.cpp: + (JSC::DEFINE_STUB_FUNCTION): + * jit/JITStubs.h: + (JSC): + * jsc.cpp: + (GlobalObject::finishCreation): + * llint/LLIntSlowPaths.cpp: + (JSC::LLInt::jitCompileAndSetHeuristics): + (JSC::LLInt::LLINT_SLOW_PATH_DECL): + * llint/LowLevelInterpreter.asm: + * llint/LowLevelInterpreter32_64.asm: + * llint/LowLevelInterpreter64.asm: + * offlineasm/x86.rb: + * runtime/ArrayConstructor.cpp: + (JSC::constructArrayWithSizeQuirk): + * runtime/ArrayConstructor.h: + (JSC): + * runtime/ArrayPrototype.cpp: + (JSC::arrayProtoFuncConcat): + (JSC::arrayProtoFuncSlice): + (JSC::arrayProtoFuncSplice): + (JSC::arrayProtoFuncFilter): + (JSC::arrayProtoFuncMap): + * runtime/Butterfly.h: + (JSC::Butterfly::contiguousInt32): + (JSC::Butterfly::contiguousDouble): + (JSC::Butterfly::fromContiguous): + * runtime/ButterflyInlineMethods.h: + (JSC::Butterfly::createUninitializedDuringCollection): + * runtime/FunctionPrototype.cpp: + (JSC::functionProtoFuncBind): + * runtime/IndexingHeaderInlineMethods.h: + (JSC::IndexingHeader::indexingPayloadSizeInBytes): + * runtime/IndexingType.cpp: + (JSC::leastUpperBoundOfIndexingTypes): + (JSC): + (JSC::leastUpperBoundOfIndexingTypeAndType): + (JSC::leastUpperBoundOfIndexingTypeAndValue): + (JSC::indexingTypeToString): + * runtime/IndexingType.h: + (JSC): + (JSC::hasUndecided): + (JSC::hasInt32): + (JSC::hasDouble): + * runtime/JSArray.cpp: + (JSC::JSArray::setLength): + (JSC::JSArray::pop): + (JSC::JSArray::push): + (JSC::JSArray::shiftCountWithAnyIndexingType): + (JSC::JSArray::unshiftCountWithAnyIndexingType): + (JSC::compareNumbersForQSortWithInt32): + (JSC): + (JSC::compareNumbersForQSortWithDouble): + (JSC::JSArray::sortNumericVector): + (JSC::JSArray::sortNumeric): + (JSC::JSArray::sortCompactedVector): + (JSC::JSArray::sort): + (JSC::JSArray::sortVector): + (JSC::JSArray::fillArgList): + (JSC::JSArray::copyToArguments): + (JSC::JSArray::compactForSorting): + * runtime/JSArray.h: + (JSArray): + (JSC::createContiguousArrayButterfly): + (JSC::JSArray::create): + (JSC::JSArray::tryCreateUninitialized): + * runtime/JSGlobalObject.cpp: + (JSC::JSGlobalObject::reset): + (JSC): + (JSC::JSGlobalObject::haveABadTime): + (JSC::JSGlobalObject::visitChildren): + * runtime/JSGlobalObject.h: + (JSGlobalObject): + (JSC::JSGlobalObject::originalArrayStructureForIndexingType): + (JSC::JSGlobalObject::arrayStructureForIndexingTypeDuringAllocation): + (JSC::JSGlobalObject::arrayStructureForProfileDuringAllocation): + (JSC::JSGlobalObject::isOriginalArrayStructure): + (JSC::constructEmptyArray): + (JSC::constructArray): + * runtime/JSObject.cpp: + (JSC::JSObject::copyButterfly): + (JSC::JSObject::getOwnPropertySlotByIndex): + (JSC::JSObject::putByIndex): + (JSC::JSObject::enterDictionaryIndexingMode): + (JSC::JSObject::createInitialIndexedStorage): + (JSC): + (JSC::JSObject::createInitialUndecided): + (JSC::JSObject::createInitialInt32): + (JSC::JSObject::createInitialDouble): + (JSC::JSObject::createInitialContiguous): + (JSC::JSObject::convertUndecidedToInt32): + (JSC::JSObject::convertUndecidedToDouble): + (JSC::JSObject::convertUndecidedToContiguous): + (JSC::JSObject::constructConvertedArrayStorageWithoutCopyingElements): + (JSC::JSObject::convertUndecidedToArrayStorage): + (JSC::JSObject::convertInt32ToDouble): + (JSC::JSObject::convertInt32ToContiguous): + (JSC::JSObject::convertInt32ToArrayStorage): + (JSC::JSObject::convertDoubleToContiguous): + (JSC::JSObject::convertDoubleToArrayStorage): + (JSC::JSObject::convertContiguousToArrayStorage): + (JSC::JSObject::convertUndecidedForValue): + (JSC::JSObject::convertInt32ForValue): + (JSC::JSObject::setIndexQuicklyToUndecided): + (JSC::JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex): + (JSC::JSObject::convertDoubleToContiguousWhilePerformingSetIndex): + (JSC::JSObject::ensureInt32Slow): + (JSC::JSObject::ensureDoubleSlow): + (JSC::JSObject::ensureContiguousSlow): + (JSC::JSObject::ensureArrayStorageSlow): + (JSC::JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode): + (JSC::JSObject::switchToSlowPutArrayStorage): + (JSC::JSObject::deletePropertyByIndex): + (JSC::JSObject::getOwnPropertyNames): + (JSC::JSObject::putByIndexBeyondVectorLengthWithoutAttributes): + (JSC::JSObject::putByIndexBeyondVectorLength): + (JSC::JSObject::putDirectIndexBeyondVectorLength): + (JSC::JSObject::getNewVectorLength): + (JSC::JSObject::countElements): + (JSC::JSObject::ensureLengthSlow): + (JSC::JSObject::getOwnPropertyDescriptor): + * runtime/JSObject.h: + (JSC::JSObject::getArrayLength): + (JSC::JSObject::getVectorLength): + (JSC::JSObject::canGetIndexQuickly): + (JSC::JSObject::getIndexQuickly): + (JSC::JSObject::tryGetIndexQuickly): + (JSC::JSObject::canSetIndexQuickly): + (JSC::JSObject::canSetIndexQuicklyForPutDirect): + (JSC::JSObject::setIndexQuickly): + (JSC::JSObject::initializeIndex): + (JSC::JSObject::hasSparseMap): + (JSC::JSObject::inSparseIndexingMode): + (JSObject): + (JSC::JSObject::ensureInt32): + (JSC::JSObject::ensureDouble): + (JSC::JSObject::ensureLength): + (JSC::JSObject::indexingData): + (JSC::JSObject::currentIndexingData): + (JSC::JSObject::getHolyIndexQuickly): + (JSC::JSObject::relevantLength): + (JSC::JSObject::currentRelevantLength): + * runtime/JSValue.cpp: + (JSC::JSValue::description): + * runtime/LiteralParser.cpp: + (JSC::::parse): + * runtime/ObjectConstructor.cpp: + (JSC::objectConstructorGetOwnPropertyNames): + (JSC::objectConstructorKeys): + * runtime/StringPrototype.cpp: + (JSC::stringProtoFuncMatch): + (JSC::stringProtoFuncSplit): + * runtime/Structure.cpp: + (JSC::Structure::nonPropertyTransition): + * runtime/StructureTransitionTable.h: + (JSC::newIndexingType): + 2012-11-08 Balazs Kilvady <kilvadyb@homejinni.com> ASSERT problem on MIPS diff --git a/Source/JavaScriptCore/Configurations/FeatureDefines.xcconfig b/Source/JavaScriptCore/Configurations/FeatureDefines.xcconfig index a4f8ca0c0..0e1b15340 100644 --- a/Source/JavaScriptCore/Configurations/FeatureDefines.xcconfig +++ b/Source/JavaScriptCore/Configurations/FeatureDefines.xcconfig @@ -42,7 +42,6 @@ ENABLE_CSS_COMPOSITING = ENABLE_CSS_COMPOSITING; ENABLE_CSS_DEVICE_ADAPTATION = ; ENABLE_CSS_EXCLUSIONS = ENABLE_CSS_EXCLUSIONS; ENABLE_CSS_FILTERS = ENABLE_CSS_FILTERS; -ENABLE_CSS_HIERARCHIES = ; ENABLE_CSS_IMAGE_ORIENTATION = ; ENABLE_CSS_IMAGE_RESOLUTION = ; ENABLE_CSS_REGIONS = ENABLE_CSS_REGIONS; @@ -129,6 +128,7 @@ ENABLE_PDFKIT_PLUGIN_macosx_1070 = ; ENABLE_PDFKIT_PLUGIN_macosx_1080 = ; ENABLE_PDFKIT_PLUGIN_macosx_1090 = ENABLE_PDFKIT_PLUGIN; ENABLE_PROGRESS_ELEMENT = ENABLE_PROGRESS_ELEMENT; +ENABLE_PROXIMITY_EVENTS = ; ENABLE_QUOTA = ; ENABLE_REQUEST_ANIMATION_FRAME = ENABLE_REQUEST_ANIMATION_FRAME; ENABLE_RESOLUTION_MEDIA_QUERY = ; @@ -157,4 +157,4 @@ ENABLE_WORKERS = ENABLE_WORKERS; ENABLE_XHR_TIMEOUT = ENABLE_XHR_TIMEOUT; ENABLE_XSLT = ENABLE_XSLT; -FEATURE_DEFINES = $(ENABLE_3D_RENDERING) $(ENABLE_ACCELERATED_2D_CANVAS) $(ENABLE_ANIMATION_API) $(ENABLE_BLOB) $(ENABLE_CHANNEL_MESSAGING) $(ENABLE_CSP_NEXT) $(ENABLE_CSS_BOX_DECORATION_BREAK) $(ENABLE_CSS_DEVICE_ADAPTATION) $(ENABLE_CSS_EXCLUSIONS) $(ENABLE_CSS_FILTERS) $(ENABLE_CSS_HIERARCHIES) $(ENABLE_CSS_IMAGE_ORIENTATION) $(ENABLE_CSS_IMAGE_RESOLUTION) $(ENABLE_CSS_REGIONS) $(ENABLE_CSS_SHADERS) $(ENABLE_CSS_COMPOSITING) $(ENABLE_CSS_STICKY_POSITION) $(ENABLE_CSS_VARIABLES) $(ENABLE_CSS3_CONDITIONAL_RULES) $(ENABLE_CSS3_TEXT) $(ENABLE_CUSTOM_SCHEME_HANDLER) $(ENABLE_DASHBOARD_SUPPORT) $(ENABLE_DATALIST_ELEMENT) $(ENABLE_DATA_TRANSFER_ITEMS) $(ENABLE_DETAILS_ELEMENT) $(ENABLE_DEVICE_ORIENTATION) $(ENABLE_DIALOG_ELEMENT) $(ENABLE_DIRECTORY_UPLOAD) $(ENABLE_DRAGGABLE_REGION) $(ENABLE_ENCRYPTED_MEDIA) $(ENABLE_FILE_SYSTEM) $(ENABLE_FILTERS) $(ENABLE_FULLSCREEN_API) $(ENABLE_GAMEPAD) $(ENABLE_GEOLOCATION) $(ENABLE_HIDDEN_PAGE_DOM_TIMER_THROTTLING) $(ENABLE_HIGH_DPI_CANVAS) $(ENABLE_ICONDATABASE) $(ENABLE_IFRAME_SEAMLESS) $(ENABLE_INDEXED_DATABASE) $(ENABLE_INPUT_SPEECH) $(ENABLE_INPUT_TYPE_COLOR) $(ENABLE_INPUT_TYPE_DATE) $(ENABLE_INPUT_TYPE_DATETIME) $(ENABLE_INPUT_TYPE_DATETIMELOCAL) $(ENABLE_INPUT_TYPE_MONTH) $(ENABLE_INPUT_TYPE_TIME) $(ENABLE_INPUT_TYPE_WEEK) $(ENABLE_JAVASCRIPT_DEBUGGER) $(ENABLE_LEGACY_CSS_VENDOR_PREFIXES) $(ENABLE_LEGACY_NOTIFICATIONS) $(ENABLE_LEGACY_VENDOR_PREFIXES) $(ENABLE_LEGACY_WEB_AUDIO) $(ENABLE_LINK_PREFETCH) $(ENABLE_LINK_PRERENDER) $(ENABLE_MATHML) $(ENABLE_MEDIA_SOURCE) $(ENABLE_MEDIA_STATISTICS) $(ENABLE_METER_ELEMENT) $(ENABLE_MHTML) $(ENABLE_MICRODATA) $(ENABLE_MUTATION_OBSERVERS) $(ENABLE_NAVIGATOR_CONTENT_UTILS) $(ENABLE_NOTIFICATIONS) $(ENABLE_PAGE_VISIBILITY_API) $(ENABLE_PDFKIT_PLUGIN) $(ENABLE_PROGRESS_ELEMENT) $(ENABLE_QUOTA) $(ENABLE_REQUEST_ANIMATION_FRAME) $(ENABLE_RESOLUTION_MEDIA_QUERY) $(ENABLE_SCRIPTED_SPEECH) $(ENABLE_SHADOW_DOM) $(ENABLE_SHARED_WORKERS) $(ENABLE_SQL_DATABASE) $(ENABLE_STYLE_SCOPED) $(ENABLE_SUBPIXEL_LAYOUT) $(ENABLE_SVG) $(ENABLE_SVG_DOM_OBJC_BINDINGS) $(ENABLE_SVG_FONTS) $(ENABLE_TEXT_AUTOSIZING) $(ENABLE_TEXT_NOTIFICATIONS_ONLY) $(ENABLE_TOUCH_ICON_LOADING) $(ENABLE_USERSELECT_ALL) $(ENABLE_VIDEO) $(ENABLE_VIDEO_TRACK) $(ENABLE_WEBGL) $(ENABLE_WEB_AUDIO) $(ENABLE_WEB_SOCKETS) $(ENABLE_WEB_TIMING) $(ENABLE_WORKERS) $(ENABLE_XHR_TIMEOUT) $(ENABLE_XSLT); +FEATURE_DEFINES = $(ENABLE_3D_RENDERING) $(ENABLE_ACCELERATED_2D_CANVAS) $(ENABLE_ANIMATION_API) $(ENABLE_BLOB) $(ENABLE_CHANNEL_MESSAGING) $(ENABLE_CSP_NEXT) $(ENABLE_CSS_BOX_DECORATION_BREAK) $(ENABLE_CSS_DEVICE_ADAPTATION) $(ENABLE_CSS_EXCLUSIONS) $(ENABLE_CSS_FILTERS) $(ENABLE_CSS_IMAGE_ORIENTATION) $(ENABLE_CSS_IMAGE_RESOLUTION) $(ENABLE_CSS_REGIONS) $(ENABLE_CSS_SHADERS) $(ENABLE_CSS_COMPOSITING) $(ENABLE_CSS_STICKY_POSITION) $(ENABLE_CSS_VARIABLES) $(ENABLE_CSS3_CONDITIONAL_RULES) $(ENABLE_CSS3_TEXT) $(ENABLE_CUSTOM_SCHEME_HANDLER) $(ENABLE_DASHBOARD_SUPPORT) $(ENABLE_DATALIST_ELEMENT) $(ENABLE_DATA_TRANSFER_ITEMS) $(ENABLE_DETAILS_ELEMENT) $(ENABLE_DEVICE_ORIENTATION) $(ENABLE_DIALOG_ELEMENT) $(ENABLE_DIRECTORY_UPLOAD) $(ENABLE_DRAGGABLE_REGION) $(ENABLE_ENCRYPTED_MEDIA) $(ENABLE_FILE_SYSTEM) $(ENABLE_FILTERS) $(ENABLE_FULLSCREEN_API) $(ENABLE_GAMEPAD) $(ENABLE_GEOLOCATION) $(ENABLE_HIDDEN_PAGE_DOM_TIMER_THROTTLING) $(ENABLE_HIGH_DPI_CANVAS) $(ENABLE_ICONDATABASE) $(ENABLE_IFRAME_SEAMLESS) $(ENABLE_INDEXED_DATABASE) $(ENABLE_INPUT_SPEECH) $(ENABLE_INPUT_TYPE_COLOR) $(ENABLE_INPUT_TYPE_DATE) $(ENABLE_INPUT_TYPE_DATETIME) $(ENABLE_INPUT_TYPE_DATETIMELOCAL) $(ENABLE_INPUT_TYPE_MONTH) $(ENABLE_INPUT_TYPE_TIME) $(ENABLE_INPUT_TYPE_WEEK) $(ENABLE_JAVASCRIPT_DEBUGGER) $(ENABLE_LEGACY_CSS_VENDOR_PREFIXES) $(ENABLE_LEGACY_NOTIFICATIONS) $(ENABLE_LEGACY_VENDOR_PREFIXES) $(ENABLE_LEGACY_WEB_AUDIO) $(ENABLE_LINK_PREFETCH) $(ENABLE_LINK_PRERENDER) $(ENABLE_MATHML) $(ENABLE_MEDIA_SOURCE) $(ENABLE_MEDIA_STATISTICS) $(ENABLE_METER_ELEMENT) $(ENABLE_MHTML) $(ENABLE_MICRODATA) $(ENABLE_MUTATION_OBSERVERS) $(ENABLE_NAVIGATOR_CONTENT_UTILS) $(ENABLE_NOTIFICATIONS) $(ENABLE_PAGE_VISIBILITY_API) $(ENABLE_PDFKIT_PLUGIN) $(ENABLE_PROGRESS_ELEMENT) $(ENABLE_PROXIMITY_EVENTS) $(ENABLE_QUOTA) $(ENABLE_REQUEST_ANIMATION_FRAME) $(ENABLE_RESOLUTION_MEDIA_QUERY) $(ENABLE_SCRIPTED_SPEECH) $(ENABLE_SHADOW_DOM) $(ENABLE_SHARED_WORKERS) $(ENABLE_SQL_DATABASE) $(ENABLE_STYLE_SCOPED) $(ENABLE_SUBPIXEL_LAYOUT) $(ENABLE_SVG) $(ENABLE_SVG_DOM_OBJC_BINDINGS) $(ENABLE_SVG_FONTS) $(ENABLE_TEXT_AUTOSIZING) $(ENABLE_TEXT_NOTIFICATIONS_ONLY) $(ENABLE_TOUCH_ICON_LOADING) $(ENABLE_USERSELECT_ALL) $(ENABLE_VIDEO) $(ENABLE_VIDEO_TRACK) $(ENABLE_WEBGL) $(ENABLE_WEB_AUDIO) $(ENABLE_WEB_SOCKETS) $(ENABLE_WEB_TIMING) $(ENABLE_WORKERS) $(ENABLE_XHR_TIMEOUT) $(ENABLE_XSLT); diff --git a/Source/JavaScriptCore/Configurations/Version.xcconfig b/Source/JavaScriptCore/Configurations/Version.xcconfig index f2d37774e..4f8baa4f2 100644 --- a/Source/JavaScriptCore/Configurations/Version.xcconfig +++ b/Source/JavaScriptCore/Configurations/Version.xcconfig @@ -22,7 +22,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. MAJOR_VERSION = 537; -MINOR_VERSION = 19; +MINOR_VERSION = 20; TINY_VERSION = 0; FULL_VERSION = $(MAJOR_VERSION).$(MINOR_VERSION); diff --git a/Source/JavaScriptCore/GNUmakefile.list.am b/Source/JavaScriptCore/GNUmakefile.list.am index d68a22b9f..d7afaf18a 100644 --- a/Source/JavaScriptCore/GNUmakefile.list.am +++ b/Source/JavaScriptCore/GNUmakefile.list.am @@ -83,6 +83,8 @@ javascriptcore_sources += \ Source/JavaScriptCore/assembler/RepatchBuffer.h \ Source/JavaScriptCore/assembler/SH4Assembler.h \ Source/JavaScriptCore/assembler/X86Assembler.h \ + Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp \ + Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h \ Source/JavaScriptCore/bytecode/ArrayProfile.cpp \ Source/JavaScriptCore/bytecode/ArrayProfile.h \ Source/JavaScriptCore/bytecode/ByValInfo.h \ @@ -162,6 +164,7 @@ javascriptcore_sources += \ Source/JavaScriptCore/dfg/DFGAssemblyHelpers.cpp \ Source/JavaScriptCore/dfg/DFGAssemblyHelpers.h \ Source/JavaScriptCore/dfg/DFGBasicBlock.h \ + Source/JavaScriptCore/dfg/DFGBranchDirection.h \ Source/JavaScriptCore/dfg/DFGByteCodeCache.h \ Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp \ Source/JavaScriptCore/dfg/DFGByteCodeParser.h \ @@ -249,14 +252,15 @@ javascriptcore_sources += \ Source/JavaScriptCore/dfg/DFGVariableAccessData.h \ Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp \ Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.h \ + Source/JavaScriptCore/disassembler/Disassembler.cpp \ Source/JavaScriptCore/disassembler/Disassembler.h \ Source/JavaScriptCore/heap/CopiedAllocator.h \ Source/JavaScriptCore/heap/CopiedBlock.h \ Source/JavaScriptCore/heap/CopiedSpace.cpp \ Source/JavaScriptCore/heap/CopiedSpace.h \ - Source/JavaScriptCore/heap/CopiedSpaceInlineMethods.h \ + Source/JavaScriptCore/heap/CopiedSpaceInlines.h \ Source/JavaScriptCore/heap/CopyVisitor.h \ - Source/JavaScriptCore/heap/CopyVisitorInlineMethods.h \ + Source/JavaScriptCore/heap/CopyVisitorInlines.h \ Source/JavaScriptCore/heap/CopyVisitor.cpp \ Source/JavaScriptCore/heap/CardSet.h \ Source/JavaScriptCore/heap/ConservativeRoots.cpp \ @@ -274,7 +278,7 @@ javascriptcore_sources += \ Source/JavaScriptCore/heap/IncrementalSweeper.cpp \ Source/JavaScriptCore/heap/SlotVisitor.cpp \ Source/JavaScriptCore/heap/SlotVisitor.h \ - Source/JavaScriptCore/heap/SlotVisitorInlineMethods.h \ + Source/JavaScriptCore/heap/SlotVisitorInlines.h \ Source/JavaScriptCore/heap/HandleStack.cpp \ Source/JavaScriptCore/heap/HandleStack.h \ Source/JavaScriptCore/heap/HandleTypes.h \ @@ -297,7 +301,7 @@ javascriptcore_sources += \ Source/JavaScriptCore/heap/MachineStackMarker.h \ Source/JavaScriptCore/heap/MarkStack.cpp \ Source/JavaScriptCore/heap/MarkStack.h \ - Source/JavaScriptCore/heap/MarkStackInlineMethods.h \ + Source/JavaScriptCore/heap/MarkStackInlines.h \ Source/JavaScriptCore/heap/HeapRootVisitor.h \ Source/JavaScriptCore/heap/MarkedAllocator.cpp \ Source/JavaScriptCore/heap/MarkedAllocator.h \ @@ -381,6 +385,8 @@ javascriptcore_sources += \ Source/JavaScriptCore/interpreter/VMInspector.h \ Source/JavaScriptCore/JavaScriptCorePrefix.h \ Source/JavaScriptCore/jit/CompactJITCodeMap.h \ + Source/JavaScriptCore/jit/ClosureCallStubRoutine.cpp \ + Source/JavaScriptCore/jit/ClosureCallStubRoutine.h \ Source/JavaScriptCore/jit/ExecutableAllocator.cpp \ Source/JavaScriptCore/jit/ExecutableAllocator.h \ Source/JavaScriptCore/jit/GCAwareJITStubRoutine.cpp \ @@ -393,12 +399,14 @@ javascriptcore_sources += \ Source/JavaScriptCore/jit/JITCall.cpp \ Source/JavaScriptCore/jit/JITCode.h \ Source/JavaScriptCore/jit/JITCompilationEffort.h \ + Source/JavaScriptCore/jit/JITDisassembler.cpp \ + Source/JavaScriptCore/jit/JITDisassembler.h \ Source/JavaScriptCore/jit/JITDriver.h \ Source/JavaScriptCore/jit/JIT.cpp \ Source/JavaScriptCore/jit/JIT.h \ Source/JavaScriptCore/jit/JITExceptions.cpp \ Source/JavaScriptCore/jit/JITExceptions.h \ - Source/JavaScriptCore/jit/JITInlineMethods.h \ + Source/JavaScriptCore/jit/JITInlines.h \ Source/JavaScriptCore/jit/JITOpcodes32_64.cpp \ Source/JavaScriptCore/jit/JITOpcodes.cpp \ Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp \ @@ -481,7 +489,7 @@ javascriptcore_sources += \ Source/JavaScriptCore/runtime/BooleanObject.h \ Source/JavaScriptCore/runtime/BooleanPrototype.cpp \ Source/JavaScriptCore/runtime/BooleanPrototype.h \ - Source/JavaScriptCore/runtime/ButterflyInlineMethods.h \ + Source/JavaScriptCore/runtime/ButterflyInlines.h \ Source/JavaScriptCore/runtime/Butterfly.h \ Source/JavaScriptCore/runtime/CachedTranscendentalFunction.h \ Source/JavaScriptCore/runtime/CallData.cpp \ @@ -529,7 +537,7 @@ javascriptcore_sources += \ Source/JavaScriptCore/runtime/GetterSetter.h \ Source/JavaScriptCore/runtime/Identifier.cpp \ Source/JavaScriptCore/runtime/Identifier.h \ - Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h \ + Source/JavaScriptCore/runtime/IndexingHeaderInlines.h \ Source/JavaScriptCore/runtime/IndexingHeader.h \ Source/JavaScriptCore/runtime/IndexingType.cpp \ Source/JavaScriptCore/runtime/IndexingType.h \ @@ -590,7 +598,7 @@ javascriptcore_sources += \ Source/JavaScriptCore/runtime/JSTypeInfo.h \ Source/JavaScriptCore/runtime/JSValue.cpp \ Source/JavaScriptCore/runtime/JSValue.h \ - Source/JavaScriptCore/runtime/JSValueInlineMethods.h \ + Source/JavaScriptCore/runtime/JSValueInlines.h \ Source/JavaScriptCore/runtime/JSVariableObject.cpp \ Source/JavaScriptCore/runtime/JSVariableObject.h \ Source/JavaScriptCore/runtime/JSWithScope.h \ diff --git a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def index b23100547..94b8c2371 100755 --- a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def +++ b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def @@ -54,6 +54,7 @@ EXPORTS ?absoluteTimeToWaitTimeoutInterval@WTF@@YAKN@Z ?activityCallback@Heap@JSC@@QAEPAVGCActivityCallback@2@XZ ?add@AtomicString@WTF@@CA?AV?$PassRefPtr@VStringImpl@WTF@@@2@PBD@Z + ?addFromLiteralData@AtomicString@WTF@@CA?AV?$PassRefPtr@VStringImpl@WTF@@@2@PBDI@Z ?add@Identifier@JSC@@CA?AV?$PassRefPtr@VStringImpl@WTF@@@WTF@@PAVJSGlobalData@2@PAVStringImpl@4@@Z ?add@Identifier@JSC@@SA?AV?$PassRefPtr@VStringImpl@WTF@@@WTF@@PAVExecState@2@PBD@Z ?add@PropertyNameArray@JSC@@QAEXPAVStringImpl@WTF@@@Z @@ -110,11 +111,12 @@ EXPORTS ?computeHash@SHA1@WTF@@QAEXAAV?$Vector@E$0BE@@2@@Z ?configurable@PropertyDescriptor@JSC@@QBE_NXZ ?construct@JSC@@YAPAVJSObject@1@PAVExecState@1@VJSValue@1@W4ConstructType@1@ABTConstructData@1@ABVArgList@1@@Z - ?constructArray@JSC@@YAPAVJSArray@1@PAVExecState@1@ABVArgList@1@@Z ?constructEmptyObject@JSC@@YAPAVJSObject@1@PAVExecState@1@@Z ?constructFunctionSkippingEvalEnabledCheck@JSC@@YAPAVJSObject@1@PAVExecState@1@PAVJSGlobalObject@1@ABVArgList@1@ABVIdentifier@1@ABVString@WTF@@ABVTextPosition@8@@Z ?constructNumber@JSC@@YAPAVNumberObject@1@PAVExecState@1@PAVJSGlobalObject@1@VJSValue@1@@Z ?constructString@JSC@@YAPAVStringObject@1@PAVExecState@1@PAVJSGlobalObject@1@VJSValue@1@@Z + ?convertDoubleToContiguousWhilePerformingSetIndex@JSObject@JSC@@AAEXAAVJSGlobalData@2@IVJSValue@2@@Z + ?convertInt32ToDoubleOrContiguousWhilePerformingSetIndex@JSObject@JSC@@AAEXAAVJSGlobalData@2@IVJSValue@2@@Z ?convertLatin1ToUTF8@Unicode@WTF@@YA?AW4ConversionResult@12@PAPBEPBEPAPADPAD@Z ?convertUTF16ToUTF8@Unicode@WTF@@YA?AW4ConversionResult@12@PAPB_WPB_WPAPADPAD_N@Z ?convertUTF8ToUTF16@Unicode@WTF@@YA?AW4ConversionResult@12@PAPBDPBDPAPA_WPA_WPA_N_N@Z @@ -146,7 +148,7 @@ EXPORTS ?currentTime@WTF@@YANXZ ?customHasInstance@JSCell@JSC@@KA_NPAVJSObject@2@PAVExecState@2@VJSValue@2@@Z ?data@CString@WTF@@QBEPBDXZ - ?dataLog@WTF@@YAXPBDZZ + ?dataLogF@WTF@@YAXPBDZZ ?dateToDaysFrom1970@WTF@@YANHHH@Z ?dayInMonthFromDayInYear@WTF@@YAHH_N@Z ?dayInYear@WTF@@YAHNH@Z @@ -312,6 +314,7 @@ EXPORTS ?releaseDecommitted@OSAllocator@WTF@@SAXPAXI@Z ?releaseExecutableMemory@JSGlobalData@JSC@@QAEXXZ ?removeBlock@MarkedAllocator@JSC@@QAEXPAVMarkedBlock@2@@Z + ?removeDirect@JSObject@JSC@@QAE_NAAVJSGlobalData@2@VPropertyName@2@@Z ?reportAbandonedObjectGraph@Heap@JSC@@QAEXXZ ?reportExtraMemoryCostSlowCase@Heap@JSC@@AAEXI@Z ?reportSuccess@HeapStatistics@JSC@@SAXXZ @@ -333,6 +336,7 @@ EXPORTS ?setGarbageCollectionTimerEnabled@Heap@JSC@@QAEX_N@Z ?setGetter@PropertyDescriptor@JSC@@QAEXVJSValue@2@@Z ?setGlobalThis@JSGlobalObject@JSC@@IAEXAAVJSGlobalData@2@PAVJSObject@2@@Z + ?setIndexQuicklyToUndecided@JSObject@JSC@@AAEXAAVJSGlobalData@2@IVJSValue@2@@Z ?setLoc@StatementNode@JSC@@QAEXHH@Z ?setMainThreadCallbacksPaused@WTF@@YAX_N@Z ?setOption@Options@JSC@@SA_NPBD@Z @@ -402,6 +406,7 @@ EXPORTS ?unlock@Mutex@WTF@@QAEXXZ ?unlockAtomicallyInitializedStaticMutex@WTF@@YAXXZ ?unprotect@Heap@JSC@@QAE_NVJSValue@2@@Z + ?updateIndexingType@ArrayAllocationProfile@JSC@@QAEXXZ ?validate@SlotVisitor@JSC@@CAXPAVJSCell@2@@Z ?visitChildren@JSGlobalObject@JSC@@SAXPAVJSCell@2@AAVSlotVisitor@2@@Z ?visitChildren@JSObject@JSC@@SAXPAVJSCell@2@AAVSlotVisitor@2@@Z diff --git a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj index b1567e2cd..d26a3b645 100644 --- a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj +++ b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj @@ -538,7 +538,7 @@ >
</File>
<File
- RelativePath="..\..\runtime\ButterflyInlineMethods.h"
+ RelativePath="..\..\runtime\ButterflyInlines.h"
>
</File>
<File
@@ -546,7 +546,7 @@ >
</File>
<File
- RelativePath="..\..\runtime\IndexingHeaderInlineMethods.h"
+ RelativePath="..\..\runtime\IndexingHeaderInlines.h"
>
</File>
<File
@@ -1010,7 +1010,7 @@ >
</File>
<File
- RelativePath="..\..\runtime\JSValueInlineMethods.h"
+ RelativePath="..\..\runtime\JSValueInlines.h"
>
</File>
<File
@@ -1582,6 +1582,14 @@ Name="bytecode"
>
<File
+ RelativePath="..\..\bytecode\ArrayAllocationProfile.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\bytecode\ArrayAllocationProfile.h"
+ >
+ </File>
+ <File
RelativePath="..\..\bytecode\ArrayProfile.cpp"
>
</File>
@@ -1922,6 +1930,14 @@ Name="jit"
>
<File
+ RelativePath="..\..\jit\ClosureCallStubRoutine.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\jit\ClosureCallStubRoutine.h"
+ >
+ </File>
+ <File
RelativePath="..\..\jit\ExecutableAllocator.cpp"
>
</File>
@@ -1970,6 +1986,14 @@ >
</File>
<File
+ RelativePath="..\..\jit\JITDisassembler.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\jit\JITDisassembler.h"
+ >
+ </File>
+ <File
RelativePath="..\..\jit\JITExceptions.cpp"
>
</File>
@@ -1978,7 +2002,7 @@ >
</File>
<File
- RelativePath="..\..\jit\JITInlineMethods.h"
+ RelativePath="..\..\jit\JITInlines.h"
>
</File>
<File
@@ -2118,6 +2142,10 @@ Name="disassembler"
>
<File
+ RelativePath="..\..\disassembler\Disassembler.cpp"
+ >
+ </File>
+ <File
RelativePath="..\..\disassembler\Disassembler.h"
>
</File>
@@ -2354,7 +2382,7 @@ >
</File>
<File
- RelativePath="..\..\heap\CopiedSpaceInlineMethods.h"
+ RelativePath="..\..\heap\CopiedSpaceInlines.h"
>
</File>
<File
@@ -2366,7 +2394,7 @@ >
</File>
<File
- RelativePath="..\..\heap\CopyVisitorInlineMethods.h"
+ RelativePath="..\..\heap\CopyVisitorInlines.h"
>
</File>
<File
@@ -2554,7 +2582,7 @@ >
</File>
<File
- RelativePath="..\..\heap\MarkStackInlineMethods.h"
+ RelativePath="..\..\heap\MarkStackInlines.h"
>
</File>
<File
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj index 3cada1cd7..bda9b27ff 100644 --- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj +++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj @@ -147,6 +147,8 @@ 0F63948515E4811B006A597C /* DFGArrayMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F63948215E48114006A597C /* DFGArrayMode.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F66E16B14DF3F1600B7B2E4 /* DFGAdjacencyList.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F66E16814DF3F1300B7B2E4 /* DFGAdjacencyList.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F66E16C14DF3F1600B7B2E4 /* DFGEdge.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F66E16914DF3F1300B7B2E4 /* DFGEdge.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 0F73D7AE165A142D00ACAB71 /* ClosureCallStubRoutine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F73D7AB165A142A00ACAB71 /* ClosureCallStubRoutine.cpp */; }; + 0F73D7AF165A143000ACAB71 /* ClosureCallStubRoutine.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F73D7AC165A142A00ACAB71 /* ClosureCallStubRoutine.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F766D2815A8CC1E008F363E /* JITStubRoutine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F766D2615A8CC1B008F363E /* JITStubRoutine.cpp */; }; 0F766D2B15A8CC38008F363E /* JITStubRoutineSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F766D2915A8CC34008F363E /* JITStubRoutineSet.cpp */; }; 0F766D2C15A8CC3A008F363E /* JITStubRoutineSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F766D2A15A8CC34008F363E /* JITStubRoutineSet.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -165,6 +167,9 @@ 0F7B294C14C3CD43007C3DB1 /* DFGByteCodeCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5F08CC146BE602000472A9 /* DFGByteCodeCache.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F7B294D14C3CD4C007C3DB1 /* DFGCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC0977E1469EBC400CF2442 /* DFGCommon.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F8023EA1613832B00A0BA45 /* ByValInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8023E91613832300A0BA45 /* ByValInfo.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 0F8335B71639C1E6001443B5 /* ArrayAllocationProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */; }; + 0F8335B81639C1EA001443B5 /* ArrayAllocationProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 0F8364B7164B0C110053329A /* DFGBranchDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F919D0C157EE09F004A4E7D /* JSSymbolTableObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F919D09157EE09D004A4E7D /* JSSymbolTableObject.cpp */; }; 0F919D0D157EE0A2004A4E7D /* JSSymbolTableObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F919D0A157EE09D004A4E7D /* JSSymbolTableObject.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F919D10157F3329004A4E7D /* JSSegmentedVariableObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F919D0E157F3327004A4E7D /* JSSegmentedVariableObject.cpp */; }; @@ -180,12 +185,15 @@ 0F9332A414CA7DD90085F3C6 /* PutByIdStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F93329A14CA7DC10085F3C6 /* PutByIdStatus.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F9332A514CA7DDD0085F3C6 /* StructureSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F93329B14CA7DC10085F3C6 /* StructureSet.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F963B3813FC6FE90002D9B2 /* ValueProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F963B3613FC6FDE0002D9B2 /* ValueProfile.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 0F9D3370165DBB90005AD387 /* Disassembler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F9D336E165DBB8D005AD387 /* Disassembler.cpp */; }; 0F9FC8C314E1B5FE00D52AE0 /* PolymorphicPutByIdList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F9FC8BF14E1B5FB00D52AE0 /* PolymorphicPutByIdList.cpp */; }; 0F9FC8C414E1B60000D52AE0 /* PolymorphicPutByIdList.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9FC8C014E1B5FB00D52AE0 /* PolymorphicPutByIdList.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F9FC8C514E1B60400D52AE0 /* PutKind.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F9FC8C114E1B5FB00D52AE0 /* PutKind.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FA581BA150E952C00B9A2D9 /* DFGNodeFlags.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FA581B7150E952A00B9A2D9 /* DFGNodeFlags.cpp */; }; 0FA581BB150E953000B9A2D9 /* DFGNodeFlags.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA581B8150E952A00B9A2D9 /* DFGNodeFlags.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FA581BC150E953000B9A2D9 /* DFGNodeType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA581B9150E952A00B9A2D9 /* DFGNodeType.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 0FAF7EFD165BA91B000C8455 /* JITDisassembler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FAF7EFA165BA919000C8455 /* JITDisassembler.cpp */; }; + 0FAF7EFE165BA91F000C8455 /* JITDisassembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FAF7EFB165BA919000C8455 /* JITDisassembler.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FB5467714F59B5C002C2989 /* LazyOperandValueProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB5467614F59AD1002C2989 /* LazyOperandValueProfile.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FB5467914F5C46B002C2989 /* LazyOperandValueProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FB5467814F5C468002C2989 /* LazyOperandValueProfile.cpp */; }; 0FB5467B14F5C7E1002C2989 /* MethodOfGettingAValueProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB5467A14F5C7D4002C2989 /* MethodOfGettingAValueProfile.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -193,9 +201,9 @@ 0FB7F39515ED8E4600F167B2 /* ArrayConventions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB7F38915ED8E3800F167B2 /* ArrayConventions.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FB7F39615ED8E4600F167B2 /* ArrayStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB7F38A15ED8E3800F167B2 /* ArrayStorage.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FB7F39715ED8E4600F167B2 /* Butterfly.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB7F38B15ED8E3800F167B2 /* Butterfly.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 0FB7F39815ED8E4600F167B2 /* ButterflyInlineMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB7F38C15ED8E3800F167B2 /* ButterflyInlineMethods.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 0FB7F39815ED8E4600F167B2 /* ButterflyInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB7F38C15ED8E3800F167B2 /* ButterflyInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FB7F39915ED8E4600F167B2 /* IndexingHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB7F38D15ED8E3800F167B2 /* IndexingHeader.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 0FB7F39A15ED8E4600F167B2 /* IndexingHeaderInlineMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB7F38E15ED8E3800F167B2 /* IndexingHeaderInlineMethods.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 0FB7F39A15ED8E4600F167B2 /* IndexingHeaderInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB7F38E15ED8E3800F167B2 /* IndexingHeaderInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FB7F39B15ED8E4600F167B2 /* IndexingType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB7F38F15ED8E3800F167B2 /* IndexingType.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FB7F39C15ED8E4600F167B2 /* PropertyStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB7F39015ED8E3800F167B2 /* PropertyStorage.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0FB7F39D15ED8E4600F167B2 /* Reject.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB7F39115ED8E3800F167B2 /* Reject.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -450,7 +458,7 @@ 863C6D9C1521111A00585E4E /* YarrCanonicalizeUCS2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 863C6D981521111200585E4E /* YarrCanonicalizeUCS2.cpp */; }; 8642C510151C06A90046D4EF /* RegExpCachedResult.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86F75EFB151C062F007C9BA3 /* RegExpCachedResult.cpp */; }; 8642C512151C083D0046D4EF /* RegExpMatchesArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86F75EFD151C062F007C9BA3 /* RegExpMatchesArray.cpp */; }; - 865A30F1135007E100CDB49E /* JSValueInlineMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = 865A30F0135007E100CDB49E /* JSValueInlineMethods.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 865A30F1135007E100CDB49E /* JSValueInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 865A30F0135007E100CDB49E /* JSValueInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; 865F408810E7D56300947361 /* APIShims.h in Headers */ = {isa = PBXBuildFile; fileRef = 865F408710E7D56300947361 /* APIShims.h */; settings = {ATTRIBUTES = (Private, ); }; }; 866739D213BFDE710023D87C /* BigInteger.h in Headers */ = {isa = PBXBuildFile; fileRef = 866739D013BFDE710023D87C /* BigInteger.h */; }; 866739D313BFDE710023D87C /* Uint16WithFraction.h in Headers */ = {isa = PBXBuildFile; fileRef = 866739D113BFDE710023D87C /* Uint16WithFraction.h */; }; @@ -486,7 +494,7 @@ 86C568E211A213EE0007F7F0 /* MIPSAssembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86C568DF11A213EE0007F7F0 /* MIPSAssembler.h */; settings = {ATTRIBUTES = (Private, ); }; }; 86CA032E1038E8440028A609 /* Executable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86CA032D1038E8440028A609 /* Executable.cpp */; }; 86CAFEE31035DDE60028A609 /* Executable.h in Headers */ = {isa = PBXBuildFile; fileRef = 86CAFEE21035DDE60028A609 /* Executable.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 86CC85A10EE79A4700288682 /* JITInlineMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = 86CC85A00EE79A4700288682 /* JITInlineMethods.h */; }; + 86CC85A10EE79A4700288682 /* JITInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 86CC85A00EE79A4700288682 /* JITInlines.h */; }; 86CC85A30EE79B7400288682 /* JITCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86CC85A20EE79B7400288682 /* JITCall.cpp */; }; 86CC85C40EE7A89400288682 /* JITPropertyAccess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86CC85C30EE7A89400288682 /* JITPropertyAccess.cpp */; }; 86CCEFDE0F413F8900FD7F9E /* JITCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 86CCEFDD0F413F8900FD7F9E /* JITCode.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -711,11 +719,11 @@ BCFD8C930EEB2EE700283848 /* JumpTable.h in Headers */ = {isa = PBXBuildFile; fileRef = BCFD8C910EEB2EE700283848 /* JumpTable.h */; }; C21122E115DD9AB300790E3A /* GCThreadSharedData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C21122DE15DD9AB300790E3A /* GCThreadSharedData.cpp */; }; C21122E215DD9AB300790E3A /* GCThreadSharedData.h in Headers */ = {isa = PBXBuildFile; fileRef = C21122DF15DD9AB300790E3A /* GCThreadSharedData.h */; settings = {ATTRIBUTES = (Private, ); }; }; - C21122E315DD9AB300790E3A /* MarkStackInlineMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = C21122E015DD9AB300790E3A /* MarkStackInlineMethods.h */; settings = {ATTRIBUTES = (Private, ); }; }; - C2160FE715F7E95E00942DFC /* SlotVisitorInlineMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FCB408515C0A3C30048932B /* SlotVisitorInlineMethods.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C21122E315DD9AB300790E3A /* MarkStackInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = C21122E015DD9AB300790E3A /* MarkStackInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2160FE715F7E95E00942DFC /* SlotVisitorInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FCB408515C0A3C30048932B /* SlotVisitorInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; C2239D1716262BDD005AC5FD /* CopyVisitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2239D1216262BDD005AC5FD /* CopyVisitor.cpp */; }; C2239D1816262BDD005AC5FD /* CopyVisitor.h in Headers */ = {isa = PBXBuildFile; fileRef = C2239D1316262BDD005AC5FD /* CopyVisitor.h */; settings = {ATTRIBUTES = (Private, ); }; }; - C2239D1916262BDD005AC5FD /* CopyVisitorInlineMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = C2239D1416262BDD005AC5FD /* CopyVisitorInlineMethods.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2239D1916262BDD005AC5FD /* CopyVisitorInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = C2239D1416262BDD005AC5FD /* CopyVisitorInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; C2239D1A16262BDD005AC5FD /* GCThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2239D1516262BDD005AC5FD /* GCThread.cpp */; }; C2239D1B16262BDD005AC5FD /* GCThread.h in Headers */ = {isa = PBXBuildFile; fileRef = C2239D1616262BDD005AC5FD /* GCThread.h */; }; C225494315F7DBAA0065E898 /* SlotVisitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C225494215F7DBAA0065E898 /* SlotVisitor.cpp */; }; @@ -728,7 +736,7 @@ C2A7F688160432D400F76B98 /* JSDestructibleObject.h in Headers */ = {isa = PBXBuildFile; fileRef = C2A7F687160432D400F76B98 /* JSDestructibleObject.h */; settings = {ATTRIBUTES = (Private, ); }; }; C2B916C214DA014E00CBAC86 /* MarkedAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = C2B916C114DA014E00CBAC86 /* MarkedAllocator.h */; settings = {ATTRIBUTES = (Private, ); }; }; C2B916C514DA040C00CBAC86 /* MarkedAllocator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2B916C414DA040C00CBAC86 /* MarkedAllocator.cpp */; }; - C2C8D02D14A3C6E000578E65 /* CopiedSpaceInlineMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C8D02B14A3C6B200578E65 /* CopiedSpaceInlineMethods.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C2C8D02D14A3C6E000578E65 /* CopiedSpaceInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C8D02B14A3C6B200578E65 /* CopiedSpaceInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; C2C8D03014A3CEFC00578E65 /* CopiedBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C8D02E14A3CEFC00578E65 /* CopiedBlock.h */; settings = {ATTRIBUTES = (Private, ); }; }; C2C8D03114A3CEFC00578E65 /* HeapBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = C2C8D02F14A3CEFC00578E65 /* HeapBlock.h */; settings = {ATTRIBUTES = (Private, ); }; }; C2D58C3415912FEE0021A844 /* GCActivityCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2D58C3315912FEE0021A844 /* GCActivityCallback.cpp */; }; @@ -935,6 +943,8 @@ 0F63948215E48114006A597C /* DFGArrayMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGArrayMode.h; path = dfg/DFGArrayMode.h; sourceTree = "<group>"; }; 0F66E16814DF3F1300B7B2E4 /* DFGAdjacencyList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGAdjacencyList.h; path = dfg/DFGAdjacencyList.h; sourceTree = "<group>"; }; 0F66E16914DF3F1300B7B2E4 /* DFGEdge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGEdge.h; path = dfg/DFGEdge.h; sourceTree = "<group>"; }; + 0F73D7AB165A142A00ACAB71 /* ClosureCallStubRoutine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClosureCallStubRoutine.cpp; sourceTree = "<group>"; }; + 0F73D7AC165A142A00ACAB71 /* ClosureCallStubRoutine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClosureCallStubRoutine.h; sourceTree = "<group>"; }; 0F766D1C15A5028D008F363E /* JITStubRoutine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITStubRoutine.h; sourceTree = "<group>"; }; 0F766D2615A8CC1B008F363E /* JITStubRoutine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITStubRoutine.cpp; sourceTree = "<group>"; }; 0F766D2915A8CC34008F363E /* JITStubRoutineSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITStubRoutineSet.cpp; sourceTree = "<group>"; }; @@ -951,6 +961,9 @@ 0F7700911402FF280078EB39 /* SamplingCounter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SamplingCounter.cpp; sourceTree = "<group>"; }; 0F7B294814C3CD23007C3DB1 /* DFGCCallHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGCCallHelpers.h; path = dfg/DFGCCallHelpers.h; sourceTree = "<group>"; }; 0F8023E91613832300A0BA45 /* ByValInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ByValInfo.h; sourceTree = "<group>"; }; + 0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayAllocationProfile.cpp; sourceTree = "<group>"; }; + 0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayAllocationProfile.h; sourceTree = "<group>"; }; + 0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGBranchDirection.h; path = dfg/DFGBranchDirection.h; sourceTree = "<group>"; }; 0F919D09157EE09D004A4E7D /* JSSymbolTableObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSSymbolTableObject.cpp; sourceTree = "<group>"; }; 0F919D0A157EE09D004A4E7D /* JSSymbolTableObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSSymbolTableObject.h; sourceTree = "<group>"; }; 0F919D0E157F3327004A4E7D /* JSSegmentedVariableObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSSegmentedVariableObject.cpp; sourceTree = "<group>"; }; @@ -966,12 +979,15 @@ 0F93329A14CA7DC10085F3C6 /* PutByIdStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PutByIdStatus.h; sourceTree = "<group>"; }; 0F93329B14CA7DC10085F3C6 /* StructureSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StructureSet.h; sourceTree = "<group>"; }; 0F963B3613FC6FDE0002D9B2 /* ValueProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ValueProfile.h; sourceTree = "<group>"; }; + 0F9D336E165DBB8D005AD387 /* Disassembler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Disassembler.cpp; path = disassembler/Disassembler.cpp; sourceTree = "<group>"; }; 0F9FC8BF14E1B5FB00D52AE0 /* PolymorphicPutByIdList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PolymorphicPutByIdList.cpp; sourceTree = "<group>"; }; 0F9FC8C014E1B5FB00D52AE0 /* PolymorphicPutByIdList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PolymorphicPutByIdList.h; sourceTree = "<group>"; }; 0F9FC8C114E1B5FB00D52AE0 /* PutKind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PutKind.h; sourceTree = "<group>"; }; 0FA581B7150E952A00B9A2D9 /* DFGNodeFlags.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGNodeFlags.cpp; path = dfg/DFGNodeFlags.cpp; sourceTree = "<group>"; }; 0FA581B8150E952A00B9A2D9 /* DFGNodeFlags.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGNodeFlags.h; path = dfg/DFGNodeFlags.h; sourceTree = "<group>"; }; 0FA581B9150E952A00B9A2D9 /* DFGNodeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGNodeType.h; path = dfg/DFGNodeType.h; sourceTree = "<group>"; }; + 0FAF7EFA165BA919000C8455 /* JITDisassembler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITDisassembler.cpp; sourceTree = "<group>"; }; + 0FAF7EFB165BA919000C8455 /* JITDisassembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITDisassembler.h; sourceTree = "<group>"; }; 0FB5467614F59AD1002C2989 /* LazyOperandValueProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LazyOperandValueProfile.h; sourceTree = "<group>"; }; 0FB5467814F5C468002C2989 /* LazyOperandValueProfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LazyOperandValueProfile.cpp; sourceTree = "<group>"; }; 0FB5467A14F5C7D4002C2989 /* MethodOfGettingAValueProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MethodOfGettingAValueProfile.h; sourceTree = "<group>"; }; @@ -979,9 +995,9 @@ 0FB7F38915ED8E3800F167B2 /* ArrayConventions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayConventions.h; sourceTree = "<group>"; }; 0FB7F38A15ED8E3800F167B2 /* ArrayStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayStorage.h; sourceTree = "<group>"; }; 0FB7F38B15ED8E3800F167B2 /* Butterfly.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Butterfly.h; sourceTree = "<group>"; }; - 0FB7F38C15ED8E3800F167B2 /* ButterflyInlineMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ButterflyInlineMethods.h; sourceTree = "<group>"; }; + 0FB7F38C15ED8E3800F167B2 /* ButterflyInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ButterflyInlines.h; sourceTree = "<group>"; }; 0FB7F38D15ED8E3800F167B2 /* IndexingHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IndexingHeader.h; sourceTree = "<group>"; }; - 0FB7F38E15ED8E3800F167B2 /* IndexingHeaderInlineMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IndexingHeaderInlineMethods.h; sourceTree = "<group>"; }; + 0FB7F38E15ED8E3800F167B2 /* IndexingHeaderInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IndexingHeaderInlines.h; sourceTree = "<group>"; }; 0FB7F38F15ED8E3800F167B2 /* IndexingType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IndexingType.h; sourceTree = "<group>"; }; 0FB7F39015ED8E3800F167B2 /* PropertyStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PropertyStorage.h; sourceTree = "<group>"; }; 0FB7F39115ED8E3800F167B2 /* Reject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reject.h; sourceTree = "<group>"; }; @@ -1006,7 +1022,7 @@ 0FC8150914043BD200CFA603 /* WriteBarrierSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WriteBarrierSupport.h; sourceTree = "<group>"; }; 0FC815121405118600CFA603 /* VTableSpectrum.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VTableSpectrum.cpp; sourceTree = "<group>"; }; 0FC815141405118D00CFA603 /* VTableSpectrum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VTableSpectrum.h; sourceTree = "<group>"; }; - 0FCB408515C0A3C30048932B /* SlotVisitorInlineMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SlotVisitorInlineMethods.h; sourceTree = "<group>"; }; + 0FCB408515C0A3C30048932B /* SlotVisitorInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SlotVisitorInlines.h; sourceTree = "<group>"; }; 0FD3C82014115CF800FD81CB /* DFGDriver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGDriver.cpp; path = dfg/DFGDriver.cpp; sourceTree = "<group>"; }; 0FD3C82214115D0E00FD81CB /* DFGDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGDriver.h; path = dfg/DFGDriver.h; sourceTree = "<group>"; }; 0FD81ACF154FB4EB00983E72 /* DFGDominators.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGDominators.cpp; path = dfg/DFGDominators.cpp; sourceTree = "<group>"; }; @@ -1226,7 +1242,7 @@ 863C6D981521111200585E4E /* YarrCanonicalizeUCS2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = YarrCanonicalizeUCS2.cpp; path = yarr/YarrCanonicalizeUCS2.cpp; sourceTree = "<group>"; }; 863C6D991521111200585E4E /* YarrCanonicalizeUCS2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YarrCanonicalizeUCS2.h; path = yarr/YarrCanonicalizeUCS2.h; sourceTree = "<group>"; }; 863C6D9A1521111200585E4E /* YarrCanonicalizeUCS2.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = YarrCanonicalizeUCS2.js; path = yarr/YarrCanonicalizeUCS2.js; sourceTree = "<group>"; }; - 865A30F0135007E100CDB49E /* JSValueInlineMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSValueInlineMethods.h; sourceTree = "<group>"; }; + 865A30F0135007E100CDB49E /* JSValueInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSValueInlines.h; sourceTree = "<group>"; }; 865F408710E7D56300947361 /* APIShims.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIShims.h; sourceTree = "<group>"; }; 866739D013BFDE710023D87C /* BigInteger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BigInteger.h; sourceTree = "<group>"; }; 866739D113BFDE710023D87C /* Uint16WithFraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Uint16WithFraction.h; sourceTree = "<group>"; }; @@ -1268,7 +1284,7 @@ 86C568DF11A213EE0007F7F0 /* MIPSAssembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIPSAssembler.h; sourceTree = "<group>"; }; 86CA032D1038E8440028A609 /* Executable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Executable.cpp; sourceTree = "<group>"; }; 86CAFEE21035DDE60028A609 /* Executable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Executable.h; sourceTree = "<group>"; }; - 86CC85A00EE79A4700288682 /* JITInlineMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITInlineMethods.h; sourceTree = "<group>"; }; + 86CC85A00EE79A4700288682 /* JITInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITInlines.h; sourceTree = "<group>"; }; 86CC85A20EE79B7400288682 /* JITCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITCall.cpp; sourceTree = "<group>"; }; 86CC85C30EE7A89400288682 /* JITPropertyAccess.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITPropertyAccess.cpp; sourceTree = "<group>"; }; 86CCEFDD0F413F8900FD7F9E /* JITCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITCode.h; sourceTree = "<group>"; }; @@ -1501,10 +1517,10 @@ BCFD8C910EEB2EE700283848 /* JumpTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JumpTable.h; sourceTree = "<group>"; }; C21122DE15DD9AB300790E3A /* GCThreadSharedData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GCThreadSharedData.cpp; sourceTree = "<group>"; }; C21122DF15DD9AB300790E3A /* GCThreadSharedData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCThreadSharedData.h; sourceTree = "<group>"; }; - C21122E015DD9AB300790E3A /* MarkStackInlineMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MarkStackInlineMethods.h; sourceTree = "<group>"; }; + C21122E015DD9AB300790E3A /* MarkStackInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MarkStackInlines.h; sourceTree = "<group>"; }; C2239D1216262BDD005AC5FD /* CopyVisitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CopyVisitor.cpp; sourceTree = "<group>"; }; C2239D1316262BDD005AC5FD /* CopyVisitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopyVisitor.h; sourceTree = "<group>"; }; - C2239D1416262BDD005AC5FD /* CopyVisitorInlineMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopyVisitorInlineMethods.h; sourceTree = "<group>"; }; + C2239D1416262BDD005AC5FD /* CopyVisitorInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopyVisitorInlines.h; sourceTree = "<group>"; }; C2239D1516262BDD005AC5FD /* GCThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GCThread.cpp; sourceTree = "<group>"; }; C2239D1616262BDD005AC5FD /* GCThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCThread.h; sourceTree = "<group>"; }; C225494215F7DBAA0065E898 /* SlotVisitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SlotVisitor.cpp; sourceTree = "<group>"; }; @@ -1516,7 +1532,7 @@ C2A7F687160432D400F76B98 /* JSDestructibleObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDestructibleObject.h; sourceTree = "<group>"; }; C2B916C114DA014E00CBAC86 /* MarkedAllocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MarkedAllocator.h; sourceTree = "<group>"; }; C2B916C414DA040C00CBAC86 /* MarkedAllocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MarkedAllocator.cpp; sourceTree = "<group>"; }; - C2C8D02B14A3C6B200578E65 /* CopiedSpaceInlineMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopiedSpaceInlineMethods.h; sourceTree = "<group>"; }; + C2C8D02B14A3C6B200578E65 /* CopiedSpaceInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopiedSpaceInlines.h; sourceTree = "<group>"; }; C2C8D02E14A3CEFC00578E65 /* CopiedBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopiedBlock.h; sourceTree = "<group>"; }; C2C8D02F14A3CEFC00578E65 /* HeapBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapBlock.h; sourceTree = "<group>"; }; C2D58C3315912FEE0021A844 /* GCActivityCallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GCActivityCallback.cpp; sourceTree = "<group>"; }; @@ -1735,6 +1751,7 @@ isa = PBXGroup; children = ( 0FF42733158EBD64004CB9FF /* udis86 */, + 0F9D336E165DBB8D005AD387 /* Disassembler.cpp */, 0FF4272F158EBD44004CB9FF /* Disassembler.h */, 0FF42730158EBD44004CB9FF /* UDis86Disassembler.cpp */, ); @@ -1797,6 +1814,8 @@ 1429D92C0ED22D7000B89619 /* jit */ = { isa = PBXGroup; children = ( + 0F73D7AB165A142A00ACAB71 /* ClosureCallStubRoutine.cpp */, + 0F73D7AC165A142A00ACAB71 /* ClosureCallStubRoutine.h */, 0FD82E37141AB14200179C94 /* CompactJITCodeMap.h */, A7B48DB60EE74CFC00DCBDB6 /* ExecutableAllocator.cpp */, A7B48DB50EE74CFC00DCBDB6 /* ExecutableAllocator.h */, @@ -1813,10 +1832,12 @@ 146FE51111A710430087AE66 /* JITCall32_64.cpp */, 86CCEFDD0F413F8900FD7F9E /* JITCode.h */, 0F0776BD14FF002800102332 /* JITCompilationEffort.h */, + 0FAF7EFA165BA919000C8455 /* JITDisassembler.cpp */, + 0FAF7EFB165BA919000C8455 /* JITDisassembler.h */, 0F21C26614BE5F5E00ADC64B /* JITDriver.h */, 0F46807F14BA572700BFE272 /* JITExceptions.cpp */, 0F46808014BA572700BFE272 /* JITExceptions.h */, - 86CC85A00EE79A4700288682 /* JITInlineMethods.h */, + 86CC85A00EE79A4700288682 /* JITInlines.h */, BCDD51E90FB8DF74004A8BDC /* JITOpcodes.cpp */, A71236E41195F33C00BD2174 /* JITOpcodes32_64.cpp */, 86CC85C30EE7A89400288682 /* JITPropertyAccess.cpp */, @@ -1842,7 +1863,7 @@ children = ( C2239D1216262BDD005AC5FD /* CopyVisitor.cpp */, C2239D1316262BDD005AC5FD /* CopyVisitor.h */, - C2239D1416262BDD005AC5FD /* CopyVisitorInlineMethods.h */, + C2239D1416262BDD005AC5FD /* CopyVisitorInlines.h */, C2239D1516262BDD005AC5FD /* GCThread.cpp */, C2239D1616262BDD005AC5FD /* GCThread.h */, C24D31E0161CD695002AA4DB /* HeapStatistics.cpp */, @@ -1850,7 +1871,7 @@ C225494215F7DBAA0065E898 /* SlotVisitor.cpp */, C21122DE15DD9AB300790E3A /* GCThreadSharedData.cpp */, C21122DF15DD9AB300790E3A /* GCThreadSharedData.h */, - C21122E015DD9AB300790E3A /* MarkStackInlineMethods.h */, + C21122E015DD9AB300790E3A /* MarkStackInlines.h */, C2E526BB1590EF000054E48D /* HeapTimer.cpp */, C2E526BC1590EF000054E48D /* HeapTimer.h */, C25F8BCB157544A900245B71 /* IncrementalSweeper.cpp */, @@ -1864,7 +1885,7 @@ C2C8D02E14A3CEFC00578E65 /* CopiedBlock.h */, C240305314B404C90079EB64 /* CopiedSpace.cpp */, C2EAA3F8149A830800FCE112 /* CopiedSpace.h */, - C2C8D02B14A3C6B200578E65 /* CopiedSpaceInlineMethods.h */, + C2C8D02B14A3C6B200578E65 /* CopiedSpaceInlines.h */, 0F2C556D14738F2E00121E4F /* DFGCodeBlocks.cpp */, 0F2C556E14738F2E00121E4F /* DFGCodeBlocks.h */, BCBE2CAD14E985AA000593AD /* GCAssertions.h */, @@ -1896,7 +1917,7 @@ 142D6F0F13539A4100B02E86 /* MarkStack.h */, 1497209014EB831500FEB1B7 /* PassWeak.h */, 14BA78F013AAB88F005B7C2C /* SlotVisitor.h */, - 0FCB408515C0A3C30048932B /* SlotVisitorInlineMethods.h */, + 0FCB408515C0A3C30048932B /* SlotVisitorInlines.h */, 142E3132134FF0A600AFADB5 /* Strong.h */, 145722851437E140005FDE26 /* StrongInlines.h */, 141448CC13A1783700F5BA1A /* TinyBloomFilter.h */, @@ -2100,7 +2121,7 @@ BC7952340E15EB5600A898AB /* BooleanPrototype.cpp */, BC7952350E15EB5600A898AB /* BooleanPrototype.h */, 0FB7F38B15ED8E3800F167B2 /* Butterfly.h */, - 0FB7F38C15ED8E3800F167B2 /* ButterflyInlineMethods.h */, + 0FB7F38C15ED8E3800F167B2 /* ButterflyInlines.h */, 869D04AE1193B54D00803475 /* CachedTranscendentalFunction.h */, BCA62DFE0E2826230004F30D /* CallData.cpp */, 145C507F0D9DF63B0088F6B9 /* CallData.h */, @@ -2146,7 +2167,7 @@ 933A349D038AE80F008635CE /* Identifier.cpp */, 933A349A038AE7C6008635CE /* Identifier.h */, 0FB7F38D15ED8E3800F167B2 /* IndexingHeader.h */, - 0FB7F38E15ED8E3800F167B2 /* IndexingHeaderInlineMethods.h */, + 0FB7F38E15ED8E3800F167B2 /* IndexingHeaderInlines.h */, 0F13E04C16164A1B00DC8DE7 /* IndexingType.cpp */, 0FB7F38F15ED8E3800F167B2 /* IndexingType.h */, E178636C0D9BEEC300D74E75 /* InitializeThreading.cpp */, @@ -2203,7 +2224,7 @@ 6507D2970E871E4A00D7D896 /* JSTypeInfo.h */, F692A8870255597D01FF60F7 /* JSValue.cpp */, 14ABB36E099C076400E2A24F /* JSValue.h */, - 865A30F0135007E100CDB49E /* JSValueInlineMethods.h */, + 865A30F0135007E100CDB49E /* JSValueInlines.h */, BC22A39A0E16E14800AF21C8 /* JSVariableObject.cpp */, 14F252560D08DD8D004ECFFF /* JSVariableObject.h */, 1442565F15EDE98D0066A49B /* JSWithScope.cpp */, @@ -2362,6 +2383,7 @@ 0FC0976B1468AB4A00CF2442 /* DFGAssemblyHelpers.cpp */, 0FC0976C1468AB4A00CF2442 /* DFGAssemblyHelpers.h */, 0F620170143FCD2F0068B77C /* DFGBasicBlock.h */, + 0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */, 0F5F08CC146BE602000472A9 /* DFGByteCodeCache.h */, 86EC9DB41328DF82002B2AD7 /* DFGByteCodeParser.cpp */, 86EC9DB51328DF82002B2AD7 /* DFGByteCodeParser.h */, @@ -2515,6 +2537,8 @@ 969A078F0ED1D3AE00F1F681 /* bytecode */ = { isa = PBXGroup; children = ( + 0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */, + 0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */, 0F63945115D07051006A597C /* ArrayProfile.cpp */, 0F63945215D07051006A597C /* ArrayProfile.h */, 0F21C27E14BEAA8000ADC64B /* BytecodeConventions.h */, @@ -2616,14 +2640,14 @@ C2B916C214DA014E00CBAC86 /* MarkedAllocator.h in Headers */, FE4A332015BD2E07006F54F3 /* VMInspector.h in Headers */, C2239D1816262BDD005AC5FD /* CopyVisitor.h in Headers */, - C2239D1916262BDD005AC5FD /* CopyVisitorInlineMethods.h in Headers */, + C2239D1916262BDD005AC5FD /* CopyVisitorInlines.h in Headers */, C24D31E3161CD695002AA4DB /* HeapStatistics.h in Headers */, C2A7F688160432D400F76B98 /* JSDestructibleObject.h in Headers */, FE20CE9E15F04A9500DF3430 /* LLIntCLoop.h in Headers */, C21122E215DD9AB300790E3A /* GCThreadSharedData.h in Headers */, - C2160FE715F7E95E00942DFC /* SlotVisitorInlineMethods.h in Headers */, + C2160FE715F7E95E00942DFC /* SlotVisitorInlines.h in Headers */, C2E526BE1590EF000054E48D /* HeapTimer.h in Headers */, - C21122E315DD9AB300790E3A /* MarkStackInlineMethods.h in Headers */, + C21122E315DD9AB300790E3A /* MarkStackInlines.h in Headers */, C25F8BCE157544A900245B71 /* IncrementalSweeper.h in Headers */, BC18C3E60E16F5CD00B34460 /* ArrayConstructor.h in Headers */, BC18C3E70E16F5CD00B34460 /* ArrayPrototype.h in Headers */, @@ -2636,7 +2660,7 @@ BC18C3EC0E16F5CD00B34460 /* BooleanObject.h in Headers */, C2C8D03014A3CEFC00578E65 /* CopiedBlock.h in Headers */, C2EAA3FA149A835E00FCE112 /* CopiedSpace.h in Headers */, - C2C8D02D14A3C6E000578E65 /* CopiedSpaceInlineMethods.h in Headers */, + C2C8D02D14A3C6E000578E65 /* CopiedSpaceInlines.h in Headers */, 969A07230ED1CE3300F1F681 /* BytecodeGenerator.h in Headers */, 869D04AF1193B54D00803475 /* CachedTranscendentalFunction.h in Headers */, BC18C3ED0E16F5CD00B34460 /* CallData.h in Headers */, @@ -2718,7 +2742,7 @@ BC18C4150E16F5CD00B34460 /* JavaScriptCorePrefix.h in Headers */, 1429D9300ED22D7000B89619 /* JIT.h in Headers */, 86CCEFDE0F413F8900FD7F9E /* JITCode.h in Headers */, - 86CC85A10EE79A4700288682 /* JITInlineMethods.h in Headers */, + 86CC85A10EE79A4700288682 /* JITInlines.h in Headers */, 960626960FB8EC02009798AB /* JITStubCall.h in Headers */, 14C5242B0F5355E900BA3D04 /* JITStubs.h in Headers */, A76F54A313B28AAB00EF2BCE /* JITWriteBarrier.h in Headers */, @@ -2760,7 +2784,7 @@ BC18C42A0E16F5CD00B34460 /* JSType.h in Headers */, 6507D29E0E871E5E00D7D896 /* JSTypeInfo.h in Headers */, BC18C42B0E16F5CD00B34460 /* JSValue.h in Headers */, - 865A30F1135007E100CDB49E /* JSValueInlineMethods.h in Headers */, + 865A30F1135007E100CDB49E /* JSValueInlines.h in Headers */, BC18C42C0E16F5CD00B34460 /* JSValueRef.h in Headers */, BC18C42D0E16F5CD00B34460 /* JSVariableObject.h in Headers */, A7482E93116A7CAD003B0712 /* JSWeakObjectMapRefInternal.h in Headers */, @@ -2994,9 +3018,9 @@ 0FB7F39515ED8E4600F167B2 /* ArrayConventions.h in Headers */, 0FB7F39615ED8E4600F167B2 /* ArrayStorage.h in Headers */, 0FB7F39715ED8E4600F167B2 /* Butterfly.h in Headers */, - 0FB7F39815ED8E4600F167B2 /* ButterflyInlineMethods.h in Headers */, + 0FB7F39815ED8E4600F167B2 /* ButterflyInlines.h in Headers */, 0FB7F39915ED8E4600F167B2 /* IndexingHeader.h in Headers */, - 0FB7F39A15ED8E4600F167B2 /* IndexingHeaderInlineMethods.h in Headers */, + 0FB7F39A15ED8E4600F167B2 /* IndexingHeaderInlines.h in Headers */, 0FB7F39B15ED8E4600F167B2 /* IndexingType.h in Headers */, 0FB7F39C15ED8E4600F167B2 /* PropertyStorage.h in Headers */, 0FB7F39D15ED8E4600F167B2 /* Reject.h in Headers */, @@ -3008,9 +3032,13 @@ 0FEB3ECD16237F4D00AB67AD /* TypedArrayDescriptor.h in Headers */, 0F256C361627B0AD007F2783 /* DFGCallArrayAllocatorSlowPathGenerator.h in Headers */, C2239D1B16262BDD005AC5FD /* GCThread.h in Headers */, + 0F8364B7164B0C110053329A /* DFGBranchDirection.h in Headers */, + 0F8335B81639C1EA001443B5 /* ArrayAllocationProfile.h in Headers */, A7B601821639FD2A00372BA3 /* UnlinkedCodeBlock.h in Headers */, A77F1822164088B200640A47 /* CodeCache.h in Headers */, A77F1825164192C700640A47 /* ParserModes.h in Headers */, + 0FAF7EFE165BA91F000C8455 /* JITDisassembler.h in Headers */, + 0F73D7AF165A143000ACAB71 /* ClosureCallStubRoutine.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3587,8 +3615,12 @@ C24D31E2161CD695002AA4DB /* HeapStatistics.cpp in Sources */, C2239D1716262BDD005AC5FD /* CopyVisitor.cpp in Sources */, C2239D1A16262BDD005AC5FD /* GCThread.cpp in Sources */, + 0F8335B71639C1E6001443B5 /* ArrayAllocationProfile.cpp in Sources */, A76F279415F13C9600517D67 /* UnlinkedCodeBlock.cpp in Sources */, A77F1821164088B200640A47 /* CodeCache.cpp in Sources */, + 0FAF7EFD165BA91B000C8455 /* JITDisassembler.cpp in Sources */, + 0F73D7AE165A142D00ACAB71 /* ClosureCallStubRoutine.cpp in Sources */, + 0F9D3370165DBB90005AD387 /* Disassembler.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/JavaScriptCore/Target.pri b/Source/JavaScriptCore/Target.pri index b0fcc16e7..e15b21dae 100644 --- a/Source/JavaScriptCore/Target.pri +++ b/Source/JavaScriptCore/Target.pri @@ -50,6 +50,7 @@ SOURCES += \ assembler/MacroAssembler.cpp \ assembler/MacroAssemblerARM.cpp \ assembler/MacroAssemblerSH4.cpp \ + bytecode/ArrayAllocationProfile.cpp \ bytecode/ArrayProfile.cpp \ bytecode/CallLinkInfo.cpp \ bytecode/CallLinkStatus.cpp \ @@ -139,10 +140,12 @@ SOURCES += \ dfg/DFGVariableEventStream.cpp \ dfg/DFGValidate.cpp \ dfg/DFGVirtualRegisterAllocationPhase.cpp \ + disassembler/Disassembler.cpp \ interpreter/AbstractPC.cpp \ interpreter/CallFrame.cpp \ interpreter/Interpreter.cpp \ interpreter/JSStack.cpp \ + jit/ClosureCallStubRoutine.cpp \ jit/ExecutableAllocatorFixedVMPool.cpp \ jit/ExecutableAllocator.cpp \ jit/HostCallReturnValue.cpp \ @@ -152,6 +155,7 @@ SOURCES += \ jit/JITCall.cpp \ jit/JITCall32_64.cpp \ jit/JIT.cpp \ + jit/JITDisassembler.cpp \ jit/JITExceptions.cpp \ jit/JITOpcodes.cpp \ jit/JITOpcodes32_64.cpp \ diff --git a/Source/JavaScriptCore/assembler/ARMv7Assembler.h b/Source/JavaScriptCore/assembler/ARMv7Assembler.h index e9b9fcc50..b93ec6e63 100644 --- a/Source/JavaScriptCore/assembler/ARMv7Assembler.h +++ b/Source/JavaScriptCore/assembler/ARMv7Assembler.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010, 2012 Apple Inc. All rights reserved. * Copyright (C) 2010 University of Szeged * * Redistribution and use in source and binary forms, with or without @@ -507,7 +507,7 @@ public: private: // ARMv7, Appx-A.6.3 - bool BadReg(RegisterID reg) + static bool BadReg(RegisterID reg) { return (reg == ARMRegisters::sp) || (reg == ARMRegisters::pc); } @@ -1261,6 +1261,18 @@ public: m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T3, imm.m_value.imm4, rd, imm); } + + static void revertJumpTo_movT3(void* instructionStart, RegisterID rd, ARMThumbImmediate imm) + { + ASSERT(imm.isValid()); + ASSERT(!imm.isEncodedImm()); + ASSERT(!BadReg(rd)); + + uint16_t* address = static_cast<uint16_t*>(instructionStart); + address[0] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, imm); + address[1] = twoWordOp5i6Imm4Reg4EncodedImmSecond(rd, imm); + cacheFlush(address, sizeof(uint16_t) * 2); + } ALWAYS_INLINE void mov(RegisterID rd, ARMThumbImmediate imm) { @@ -1928,7 +1940,6 @@ public: return LinkConditionalBX; const int paddingSize = JUMP_ENUM_SIZE(jumpType); - bool mayTriggerErrata = false; if (jumpType == JumpCondition) { // 2-byte conditional T1 @@ -1937,17 +1948,13 @@ public: return LinkJumpT1; // 4-byte conditional T3 const uint16_t* jumpT3Location = reinterpret_cast_ptr<const uint16_t*>(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT3))); - if (canBeJumpT3(jumpT3Location, to, mayTriggerErrata)) { - if (!mayTriggerErrata) - return LinkJumpT3; - } + if (canBeJumpT3(jumpT3Location, to)) + return LinkJumpT3; // 4-byte conditional T4 with IT const uint16_t* conditionalJumpT4Location = reinterpret_cast_ptr<const uint16_t*>(from - (paddingSize - JUMP_ENUM_SIZE(LinkConditionalJumpT4))); - if (canBeJumpT4(conditionalJumpT4Location, to, mayTriggerErrata)) { - if (!mayTriggerErrata) - return LinkConditionalJumpT4; - } + if (canBeJumpT4(conditionalJumpT4Location, to)) + return LinkConditionalJumpT4; } else { // 2-byte unconditional T2 const uint16_t* jumpT2Location = reinterpret_cast_ptr<const uint16_t*>(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT2))); @@ -1955,10 +1962,8 @@ public: return LinkJumpT2; // 4-byte unconditional T4 const uint16_t* jumpT4Location = reinterpret_cast_ptr<const uint16_t*>(from - (paddingSize - JUMP_ENUM_SIZE(LinkJumpT4))); - if (canBeJumpT4(jumpT4Location, to, mayTriggerErrata)) { - if (!mayTriggerErrata) - return LinkJumpT4; - } + if (canBeJumpT4(jumpT4Location, to)) + return LinkJumpT4; // use long jump sequence return LinkBX; } @@ -2057,12 +2062,12 @@ public: ASSERT(from.isSet()); ASSERT(reinterpret_cast<intptr_t>(to) & 1); - setPointer(reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(code) + from.m_offset) - 1, to); + setPointer(reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(code) + from.m_offset) - 1, to, false); } static void linkPointer(void* code, AssemblerLabel where, void* value) { - setPointer(reinterpret_cast<char*>(code) + where.m_offset, value); + setPointer(reinterpret_cast<char*>(code) + where.m_offset, value, false); } static void relinkJump(void* from, void* to) @@ -2080,7 +2085,7 @@ public: ASSERT(!(reinterpret_cast<intptr_t>(from) & 1)); ASSERT(reinterpret_cast<intptr_t>(to) & 1); - setPointer(reinterpret_cast<uint16_t*>(from) - 1, to); + setPointer(reinterpret_cast<uint16_t*>(from) - 1, to, true); } static void* readCallTarget(void* from) @@ -2092,7 +2097,7 @@ public: { ASSERT(!(reinterpret_cast<intptr_t>(where) & 1)); - setInt32(where, value); + setInt32(where, value, true); } static void repatchCompact(void* where, int32_t offset) @@ -2119,7 +2124,7 @@ public: { ASSERT(!(reinterpret_cast<intptr_t>(where) & 1)); - setPointer(where, value); + setPointer(where, value, true); } static void* readPointer(void* where) @@ -2133,22 +2138,13 @@ public: ASSERT(!(bitwise_cast<uintptr_t>(to) & 1)); uint16_t* ptr = reinterpret_cast<uint16_t*>(instructionStart) + 2; - // Ensure that we're not in one of those errata-triggering thingies. If we are, then - // prepend a nop. - bool spansTwo4K = ((reinterpret_cast<intptr_t>(ptr) & 0xfff) == 0x002); - - if (spansTwo4K) { - ptr[-2] = OP_NOP_T1; - ptr++; - } - linkJumpT4(ptr, to); cacheFlush(ptr - 2, sizeof(uint16_t) * 2); } static ptrdiff_t maxJumpReplacementSize() { - return 6; + return 4; } static void replaceWithLoad(void* instructionStart) @@ -2164,11 +2160,11 @@ public: ptr[0] |= OP_LDR_imm_T3; ptr[1] |= (ptr[1] & 0x0F00) << 4; ptr[1] &= 0xF0FF; + cacheFlush(ptr, sizeof(uint16_t) * 2); break; default: ASSERT_NOT_REACHED(); } - cacheFlush(ptr, sizeof(uint16_t) * 2); } static void replaceWithAddressComputation(void* instructionStart) @@ -2182,13 +2178,13 @@ public: ptr[0] |= OP_ADD_imm_T3; ptr[1] |= (ptr[1] & 0xF000) >> 4; ptr[1] &= 0x0FFF; + cacheFlush(ptr, sizeof(uint16_t) * 2); break; case OP_ADD_imm_T3: break; default: ASSERT_NOT_REACHED(); } - cacheFlush(ptr, sizeof(uint16_t) * 2); } unsigned debugOffset() { return m_formatter.debugOffset(); } @@ -2291,7 +2287,7 @@ private: return VFPOperand(op); } - static void setInt32(void* code, uint32_t value) + static void setInt32(void* code, uint32_t value, bool flush) { uint16_t* location = reinterpret_cast<uint16_t*>(code); ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); @@ -2303,7 +2299,8 @@ private: location[-2] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); location[-1] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-1] >> 8) & 0xf, hi16); - cacheFlush(location - 4, 4 * sizeof(uint16_t)); + if (flush) + cacheFlush(location - 4, 4 * sizeof(uint16_t)); } static int32_t readInt32(void* code) @@ -2334,9 +2331,9 @@ private: cacheFlush(location, sizeof(uint16_t)); } - static void setPointer(void* code, void* value) + static void setPointer(void* code, void* value, bool flush) { - setInt32(code, reinterpret_cast<uint32_t>(value)); + setInt32(code, reinterpret_cast<uint32_t>(value), flush); } static bool isB(void* address) @@ -2401,46 +2398,22 @@ private: return ((relative << 20) >> 20) == relative; } - static bool canBeJumpT3(const uint16_t* instruction, const void* target, bool& mayTriggerErrata) + static bool canBeJumpT3(const uint16_t* instruction, const void* target) { ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); - // From Cortex-A8 errata: - // If the 32-bit Thumb-2 branch instruction spans two 4KiB regions and - // the target of the branch falls within the first region it is - // possible for the processor to incorrectly determine the branch - // instruction, and it is also possible in some cases for the processor - // to enter a deadlock state. - // The instruction is spanning two pages if it ends at an address ending 0x002 - bool spansTwo4K = ((reinterpret_cast<intptr_t>(instruction) & 0xfff) == 0x002); - mayTriggerErrata = spansTwo4K; - // The target is in the first page if the jump branch back by [3..0x1002] bytes - bool targetInFirstPage = (relative >= -0x1002) && (relative < -2); - bool wouldTriggerA8Errata = spansTwo4K && targetInFirstPage; - return ((relative << 11) >> 11) == relative && !wouldTriggerA8Errata; + return ((relative << 11) >> 11) == relative; } - static bool canBeJumpT4(const uint16_t* instruction, const void* target, bool& mayTriggerErrata) + static bool canBeJumpT4(const uint16_t* instruction, const void* target) { ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); - // From Cortex-A8 errata: - // If the 32-bit Thumb-2 branch instruction spans two 4KiB regions and - // the target of the branch falls within the first region it is - // possible for the processor to incorrectly determine the branch - // instruction, and it is also possible in some cases for the processor - // to enter a deadlock state. - // The instruction is spanning two pages if it ends at an address ending 0x002 - bool spansTwo4K = ((reinterpret_cast<intptr_t>(instruction) & 0xfff) == 0x002); - mayTriggerErrata = spansTwo4K; - // The target is in the first page if the jump branch back by [3..0x1002] bytes - bool targetInFirstPage = (relative >= -0x1002) && (relative < -2); - bool wouldTriggerA8Errata = spansTwo4K && targetInFirstPage; - return ((relative << 7) >> 7) == relative && !wouldTriggerA8Errata; + return ((relative << 7) >> 7) == relative; } void linkJumpT1(Condition cond, uint16_t* instruction, void* target) @@ -2484,9 +2457,7 @@ private: // FIMXE: this should be up in the MacroAssembler layer. :-( ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); - bool scratch; - UNUSED_PARAM(scratch); - ASSERT(canBeJumpT3(instruction, target, scratch)); + ASSERT(canBeJumpT3(instruction, target)); intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); @@ -2501,9 +2472,7 @@ private: // FIMXE: this should be up in the MacroAssembler layer. :-( ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); - bool scratch; - UNUSED_PARAM(scratch); - ASSERT(canBeJumpT4(instruction, target, scratch)); + ASSERT(canBeJumpT4(instruction, target)); intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); // ARM encoding for the top two bits below the sign bit is 'peculiar'. @@ -2561,8 +2530,7 @@ private: ASSERT((isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1)) || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2))); - bool scratch; - if (canBeJumpT4(instruction, target, scratch)) { + if (canBeJumpT4(instruction, target)) { // There may be a better way to fix this, but right now put the NOPs first, since in the // case of an conditional branch this will be coming after an ITTT predicating *three* // instructions! Looking backwards to modify the ITTT to an IT is not easy, due to diff --git a/Source/JavaScriptCore/assembler/AbstractMacroAssembler.h b/Source/JavaScriptCore/assembler/AbstractMacroAssembler.h index c75adb7e9..673031b7a 100644 --- a/Source/JavaScriptCore/assembler/AbstractMacroAssembler.h +++ b/Source/JavaScriptCore/assembler/AbstractMacroAssembler.h @@ -586,6 +586,13 @@ public: public: typedef Vector<Jump, 16> JumpVector; + + JumpList() { } + + JumpList(Jump jump) + { + append(jump); + } void link(AbstractMacroAssembler<AssemblerType>* masm) { diff --git a/Source/JavaScriptCore/assembler/LinkBuffer.cpp b/Source/JavaScriptCore/assembler/LinkBuffer.cpp index 0176e4307..c269157ba 100644 --- a/Source/JavaScriptCore/assembler/LinkBuffer.cpp +++ b/Source/JavaScriptCore/assembler/LinkBuffer.cpp @@ -45,16 +45,15 @@ LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithDisassembly(const char* format, CodeRef result = finalizeCodeWithoutDisassembly(); - dataLog("Generated JIT code for "); + dataLogF("Generated JIT code for "); va_list argList; va_start(argList, format); - WTF::dataLogV(format, argList); + WTF::dataLogFV(format, argList); va_end(argList); - dataLog(":\n"); + dataLogF(":\n"); - dataLog(" Code at [%p, %p):\n", result.code().executableAddress(), static_cast<char*>(result.code().executableAddress()) + result.size()); - if (!tryToDisassemble(result.code(), m_size, " ", WTF::dataFile())) - dataLog(" <no disassembly available>\n"); + dataLogF(" Code at [%p, %p):\n", result.code().executableAddress(), static_cast<char*>(result.code().executableAddress()) + result.size()); + disassemble(result.code(), m_size, " ", WTF::dataFile()); return result; } @@ -169,11 +168,11 @@ void LinkBuffer::dumpLinkStatistics(void* code, size_t initializeSize, size_t fi linkCount++; totalInitialSize += initialSize; totalFinalSize += finalSize; - dataLog("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", + dataLogF("link %p: orig %u, compact %u (delta %u, %.2f%%)\n", code, static_cast<unsigned>(initialSize), static_cast<unsigned>(finalSize), static_cast<unsigned>(initialSize - finalSize), 100.0 * (initialSize - finalSize) / initialSize); - dataLog("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", + dataLogF("\ttotal %u: orig %u, compact %u (delta %u, %.2f%%)\n", linkCount, totalInitialSize, totalFinalSize, totalInitialSize - totalFinalSize, 100.0 * (totalInitialSize - totalFinalSize) / totalInitialSize); } @@ -192,7 +191,7 @@ void LinkBuffer::dumpCode(void* code, size_t size) size_t tsize = size / sizeof(short); char nameBuf[128]; snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); - dataLog("\t.syntax unified\n" + dataLogF("\t.syntax unified\n" "\t.section\t__TEXT,__text,regular,pure_instructions\n" "\t.globl\t%s\n" "\t.align 2\n" @@ -202,7 +201,7 @@ void LinkBuffer::dumpCode(void* code, size_t size) "%s:\n", nameBuf, nameBuf, code, nameBuf); for (unsigned i = 0; i < tsize; i++) - dataLog("\t.short\t0x%x\n", tcode[i]); + dataLogF("\t.short\t0x%x\n", tcode[i]); #elif CPU(ARM_TRADITIONAL) // gcc -c jit.s // objdump -D jit.o @@ -211,7 +210,7 @@ void LinkBuffer::dumpCode(void* code, size_t size) size_t tsize = size / sizeof(unsigned int); char nameBuf[128]; snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++); - dataLog("\t.globl\t%s\n" + dataLogF("\t.globl\t%s\n" "\t.align 4\n" "\t.code 32\n" "\t.text\n" @@ -219,7 +218,7 @@ void LinkBuffer::dumpCode(void* code, size_t size) "%s:\n", nameBuf, code, nameBuf); for (unsigned i = 0; i < tsize; i++) - dataLog("\t.long\t0x%x\n", tcode[i]); + dataLogF("\t.long\t0x%x\n", tcode[i]); #endif } #endif diff --git a/Source/JavaScriptCore/assembler/LinkBuffer.h b/Source/JavaScriptCore/assembler/LinkBuffer.h index 770144d64..e1882433c 100644 --- a/Source/JavaScriptCore/assembler/LinkBuffer.h +++ b/Source/JavaScriptCore/assembler/LinkBuffer.h @@ -263,9 +263,9 @@ private: #endif }; -#define FINALIZE_CODE_IF(condition, linkBufferReference, dataLogArgumentsForHeading) \ +#define FINALIZE_CODE_IF(condition, linkBufferReference, dataLogFArgumentsForHeading) \ (UNLIKELY((condition)) \ - ? ((linkBufferReference).finalizeCodeWithDisassembly dataLogArgumentsForHeading) \ + ? ((linkBufferReference).finalizeCodeWithDisassembly dataLogFArgumentsForHeading) \ : (linkBufferReference).finalizeCodeWithoutDisassembly()) // Use this to finalize code, like so: @@ -281,14 +281,14 @@ private: // // ... and so on. // -// Note that the dataLogArgumentsForHeading are only evaluated when showDisassembly +// Note that the dataLogFArgumentsForHeading are only evaluated when showDisassembly // is true, so you can hide expensive disassembly-only computations inside there. -#define FINALIZE_CODE(linkBufferReference, dataLogArgumentsForHeading) \ - FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, dataLogArgumentsForHeading) +#define FINALIZE_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ + FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, dataLogFArgumentsForHeading) -#define FINALIZE_DFG_CODE(linkBufferReference, dataLogArgumentsForHeading) \ - FINALIZE_CODE_IF(Options::showDFGDisassembly(), linkBufferReference, dataLogArgumentsForHeading) +#define FINALIZE_DFG_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ + FINALIZE_CODE_IF((Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, dataLogFArgumentsForHeading) } // namespace JSC diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerARM.h b/Source/JavaScriptCore/assembler/MacroAssemblerARM.h index 39d94adea..0ebdbda0c 100644 --- a/Source/JavaScriptCore/assembler/MacroAssemblerARM.h +++ b/Source/JavaScriptCore/assembler/MacroAssemblerARM.h @@ -1266,6 +1266,19 @@ public: return 0; } + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatch(CodeLocationDataLabelPtr label) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + UNREACHABLE_FOR_PLATFORM(); + } + protected: ARMAssembler::Condition ARMCondition(RelationalCondition cond) { diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h b/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h index 1301038e5..8d7a3a69a 100644 --- a/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h +++ b/Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h @@ -1758,6 +1758,30 @@ public: { return FunctionPtr(reinterpret_cast<void(*)()>(ARMv7Assembler::readCallTarget(call.dataLocation()))); } + + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + const unsigned twoWordOpSize = 4; + return label.labelAtOffset(-twoWordOpSize * 2); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + ARMv7Assembler::revertJumpTo_movT3(instructionStart.dataLocation(), dataTempRegister, ARMThumbImmediate::makeUInt16(reinterpret_cast<uintptr_t>(initialValue) & 0xffff)); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel, Address, void*) + { + UNREACHABLE_FOR_PLATFORM(); + } protected: ALWAYS_INLINE Jump jump() diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h b/Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h index fc6f9f40d..5b6da9663 100644 --- a/Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h +++ b/Source/JavaScriptCore/assembler/MacroAssemblerMIPS.h @@ -2242,6 +2242,20 @@ public: return 0; } + static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatch(CodeLocationDataLabelPtr label) + { + UNREACHABLE_FOR_PLATFORM(); + return CodeLocationLabel(); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + UNREACHABLE_FOR_PLATFORM(); + } + + private: // If m_fixedWidth is true, we will generate a fixed number of instructions. // Otherwise, we can emit any number of instructions. diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerX86.h b/Source/JavaScriptCore/assembler/MacroAssemblerX86.h index 8fd31466d..27a030edf 100644 --- a/Source/JavaScriptCore/assembler/MacroAssemblerX86.h +++ b/Source/JavaScriptCore/assembler/MacroAssemblerX86.h @@ -253,6 +253,40 @@ public: return FunctionPtr(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(call.dataLocation()) + offset)); } + static bool canJumpReplacePatchableBranchPtrWithPatch() { return true; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + const int opcodeBytes = 1; + const int modRMBytes = 1; + const int immediateBytes = 4; + const int totalBytes = opcodeBytes + modRMBytes + immediateBytes; + ASSERT(totalBytes >= maxJumpReplacementSize()); + return label.labelAtOffset(-totalBytes); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) + { + const int opcodeBytes = 1; + const int modRMBytes = 1; + const int offsetBytes = 0; + const int immediateBytes = 4; + const int totalBytes = opcodeBytes + modRMBytes + offsetBytes + immediateBytes; + ASSERT(totalBytes >= maxJumpReplacementSize()); + return label.labelAtOffset(-totalBytes); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID reg, void* initialValue) + { + X86Assembler::revertJumpTo_cmpl_ir_force32(instructionStart.executableAddress(), reinterpret_cast<intptr_t>(initialValue), reg); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address address, void* initialValue) + { + ASSERT(!address.offset); + X86Assembler::revertJumpTo_cmpl_im_force32(instructionStart.executableAddress(), reinterpret_cast<intptr_t>(initialValue), 0, address.base); + } + private: friend class LinkBuffer; friend class RepatchBuffer; diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h b/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h index 66db26acb..53cb80c21 100644 --- a/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h +++ b/Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h @@ -826,11 +826,15 @@ public: m_assembler.ucomisd_rr(right, left); if (cond == DoubleEqual) { + if (left == right) + return Jump(m_assembler.jnp()); Jump isUnordered(m_assembler.jp()); Jump result = Jump(m_assembler.je()); isUnordered.link(this); return result; } else if (cond == DoubleNotEqualOrUnordered) { + if (left == right) + return Jump(m_assembler.jp()); Jump isUnordered(m_assembler.jp()); Jump isEqual(m_assembler.je()); isUnordered.link(this); diff --git a/Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h b/Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h index ceacf6aa8..c711e6f8d 100644 --- a/Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h +++ b/Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h @@ -585,6 +585,33 @@ public: static RegisterID scratchRegisterForBlinding() { return scratchRegister; } + static bool canJumpReplacePatchableBranchPtrWithPatch() { return true; } + + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + const int rexBytes = 1; + const int opcodeBytes = 1; + const int immediateBytes = 8; + const int totalBytes = rexBytes + opcodeBytes + immediateBytes; + ASSERT(totalBytes >= maxJumpReplacementSize()); + return label.labelAtOffset(-totalBytes); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) + { + return startOfBranchPtrWithPatchOnRegister(label); + } + + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + { + X86Assembler::revertJumpTo_movq_i64r(instructionStart.executableAddress(), reinterpret_cast<intptr_t>(initialValue), scratchRegister); + } + + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue) + { + X86Assembler::revertJumpTo_movq_i64r(instructionStart.executableAddress(), reinterpret_cast<intptr_t>(initialValue), scratchRegister); + } + private: friend class LinkBuffer; friend class RepatchBuffer; diff --git a/Source/JavaScriptCore/assembler/RepatchBuffer.h b/Source/JavaScriptCore/assembler/RepatchBuffer.h index 531dda934..dbb56f9ad 100644 --- a/Source/JavaScriptCore/assembler/RepatchBuffer.h +++ b/Source/JavaScriptCore/assembler/RepatchBuffer.h @@ -141,6 +141,34 @@ public: replaceWithAddressComputation(label); } + static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label) + { + return MacroAssembler::startOfBranchPtrWithPatchOnRegister(label); + } + + static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr label) + { + return MacroAssembler::startOfPatchableBranchPtrWithPatchOnAddress(label); + } + + void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination) + { + MacroAssembler::replaceWithJump(instructionStart, destination); + } + + // This is a *bit* of a silly API, since we currently always also repatch the + // immediate after calling this. But I'm fine with that, since this just feels + // less yucky. + void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::RegisterID reg, void* value) + { + MacroAssembler::revertJumpReplacementToBranchPtrWithPatch(instructionStart, reg, value); + } + + void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, MacroAssembler::Address address, void* value) + { + MacroAssembler::revertJumpReplacementToPatchableBranchPtrWithPatch(instructionStart, address, value); + } + private: void* m_start; size_t m_size; diff --git a/Source/JavaScriptCore/assembler/SH4Assembler.h b/Source/JavaScriptCore/assembler/SH4Assembler.h index 373f469dc..3dbde2fa5 100644 --- a/Source/JavaScriptCore/assembler/SH4Assembler.h +++ b/Source/JavaScriptCore/assembler/SH4Assembler.h @@ -2070,7 +2070,7 @@ public: static void vprintfStdoutInstr(const char* format, va_list args) { if (getenv("JavaScriptCoreDumpJIT")) - WTF::dataLogV(format, args); + WTF::dataLogFV(format, args); } static void printBlockInstr(uint16_t* first, unsigned int offset, int nbInstr) diff --git a/Source/JavaScriptCore/assembler/X86Assembler.h b/Source/JavaScriptCore/assembler/X86Assembler.h index ecb178e88..25ff6f0a5 100644 --- a/Source/JavaScriptCore/assembler/X86Assembler.h +++ b/Source/JavaScriptCore/assembler/X86Assembler.h @@ -1475,6 +1475,12 @@ public: return m_formatter.immediateRel32(); } + AssemblerLabel jnp() + { + m_formatter.twoByteOp(jccRel32(ConditionNP)); + return m_formatter.immediateRel32(); + } + AssemblerLabel jp() { m_formatter.twoByteOp(jccRel32(ConditionP)); @@ -1877,6 +1883,61 @@ public: return 5; } +#if CPU(X86_64) + static void revertJumpTo_movq_i64r(void* instructionStart, int64_t imm, RegisterID dst) + { + const int rexBytes = 1; + const int opcodeBytes = 1; + ASSERT(rexBytes + opcodeBytes <= maxJumpReplacementSize()); + uint8_t* ptr = reinterpret_cast<uint8_t*>(instructionStart); + ptr[0] = PRE_REX | (1 << 3) | (dst >> 3); + ptr[1] = OP_MOV_EAXIv | (dst & 7); + + union { + uint64_t asWord; + uint8_t asBytes[8]; + } u; + u.asWord = imm; + for (unsigned i = rexBytes + opcodeBytes; i < static_cast<unsigned>(maxJumpReplacementSize()); ++i) + ptr[i] = u.asBytes[i - rexBytes - opcodeBytes]; + } +#endif + + static void revertJumpTo_cmpl_ir_force32(void* instructionStart, int32_t imm, RegisterID dst) + { + const int opcodeBytes = 1; + const int modRMBytes = 1; + ASSERT(opcodeBytes + modRMBytes <= maxJumpReplacementSize()); + uint8_t* ptr = reinterpret_cast<uint8_t*>(instructionStart); + ptr[0] = OP_GROUP1_EvIz; + ptr[1] = (X86InstructionFormatter::ModRmRegister << 6) | (GROUP1_OP_CMP << 3) | dst; + union { + uint32_t asWord; + uint8_t asBytes[4]; + } u; + u.asWord = imm; + for (unsigned i = opcodeBytes + modRMBytes; i < static_cast<unsigned>(maxJumpReplacementSize()); ++i) + ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes]; + } + + static void revertJumpTo_cmpl_im_force32(void* instructionStart, int32_t imm, int offset, RegisterID dst) + { + ASSERT_UNUSED(offset, !offset); + const int opcodeBytes = 1; + const int modRMBytes = 1; + ASSERT(opcodeBytes + modRMBytes <= maxJumpReplacementSize()); + uint8_t* ptr = reinterpret_cast<uint8_t*>(instructionStart); + ptr[0] = OP_GROUP1_EvIz; + ptr[1] = (X86InstructionFormatter::ModRmMemoryNoDisp << 6) | (GROUP1_OP_CMP << 3) | dst; + union { + uint32_t asWord; + uint8_t asBytes[4]; + } u; + u.asWord = imm; + for (unsigned i = opcodeBytes + modRMBytes; i < static_cast<unsigned>(maxJumpReplacementSize()); ++i) + ptr[i] = u.asBytes[i - opcodeBytes - modRMBytes]; + } + static void replaceWithLoad(void* instructionStart) { uint8_t* ptr = reinterpret_cast<uint8_t*>(instructionStart); @@ -1976,6 +2037,13 @@ private: public: + enum ModRmMode { + ModRmMemoryNoDisp, + ModRmMemoryDisp8, + ModRmMemoryDisp32, + ModRmRegister, + }; + // Legacy prefix bytes: // // These are emmitted prior to the instruction. @@ -2314,6 +2382,9 @@ private: // Format a REX prefix byte. inline void emitRex(bool w, int r, int x, int b) { + ASSERT(r >= 0); + ASSERT(x >= 0); + ASSERT(b >= 0); m_buffer.putByteUnchecked(PRE_REX | ((int)w << 3) | ((r>>3)<<2) | ((x>>3)<<1) | (b>>3)); } @@ -2343,13 +2414,6 @@ private: inline void emitRexIfNeeded(int, int, int) {} #endif - enum ModRmMode { - ModRmMemoryNoDisp, - ModRmMemoryDisp8, - ModRmMemoryDisp32, - ModRmRegister, - }; - void putModRm(ModRmMode mode, int reg, RegisterID rm) { m_buffer.putByteUnchecked((mode << 6) | ((reg & 7) << 3) | (rm & 7)); diff --git a/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp b/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp new file mode 100644 index 000000000..aa682da86 --- /dev/null +++ b/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ArrayAllocationProfile.h" + +namespace JSC { + +void ArrayAllocationProfile::updateIndexingType() +{ + if (!m_lastArray) + return; + m_currentIndexingType = leastUpperBoundOfIndexingTypes(m_currentIndexingType, m_lastArray->structure()->indexingType()); + m_lastArray = 0; +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h b/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h new file mode 100644 index 000000000..a1647fad4 --- /dev/null +++ b/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ArrayAllocationProfile_h +#define ArrayAllocationProfile_h + +#include "IndexingType.h" +#include "JSArray.h" + +namespace JSC { + +class ArrayAllocationProfile { +public: + ArrayAllocationProfile() + : m_currentIndexingType(ArrayWithUndecided) + , m_lastArray(0) + { + } + + IndexingType selectIndexingType() + { + if (m_lastArray && UNLIKELY(m_lastArray->structure()->indexingType() != m_currentIndexingType)) + updateIndexingType(); + return m_currentIndexingType; + } + + JSArray* updateLastAllocation(JSArray* lastArray) + { + m_lastArray = lastArray; + return lastArray; + } + + JS_EXPORT_PRIVATE void updateIndexingType(); + + static IndexingType selectIndexingTypeFor(ArrayAllocationProfile* profile) + { + if (!profile) + return ArrayWithUndecided; + return profile->selectIndexingType(); + } + + static JSArray* updateLastAllocationFor(ArrayAllocationProfile* profile, JSArray* lastArray) + { + if (profile) + profile->updateLastAllocation(lastArray); + return lastArray; + } + +private: + + IndexingType m_currentIndexingType; + JSArray* m_lastArray; +}; + +} // namespace JSC + +#endif // ArrayAllocationProfile_h + diff --git a/Source/JavaScriptCore/bytecode/ArrayProfile.cpp b/Source/JavaScriptCore/bytecode/ArrayProfile.cpp index 5a87380fd..51baf332f 100644 --- a/Source/JavaScriptCore/bytecode/ArrayProfile.cpp +++ b/Source/JavaScriptCore/bytecode/ArrayProfile.cpp @@ -65,6 +65,13 @@ const char* arrayModesToString(ArrayModes arrayModes) return result; } +ArrayModes ArrayProfile::updatedObservedArrayModes() const +{ + if (m_lastSeenStructure) + return m_observedArrayModes | arrayModeFromStructure(m_lastSeenStructure); + return m_observedArrayModes; +} + void ArrayProfile::computeUpdatedPrediction(CodeBlock* codeBlock, OperationInProgress operation) { if (m_lastSeenStructure) { diff --git a/Source/JavaScriptCore/bytecode/ArrayProfile.h b/Source/JavaScriptCore/bytecode/ArrayProfile.h index 376684fc1..5116cd36f 100644 --- a/Source/JavaScriptCore/bytecode/ArrayProfile.h +++ b/Source/JavaScriptCore/bytecode/ArrayProfile.h @@ -45,15 +45,20 @@ typedef unsigned ArrayModes; #define ALL_NON_ARRAY_ARRAY_MODES \ (asArrayModes(NonArray) \ - | asArrayModes(NonArrayWithContiguous) \ - | asArrayModes(NonArrayWithArrayStorage) \ - | asArrayModes(NonArrayWithSlowPutArrayStorage)) + | asArrayModes(NonArrayWithInt32) \ + | asArrayModes(NonArrayWithDouble) \ + | asArrayModes(NonArrayWithContiguous) \ + | asArrayModes(NonArrayWithArrayStorage) \ + | asArrayModes(NonArrayWithSlowPutArrayStorage)) #define ALL_ARRAY_ARRAY_MODES \ (asArrayModes(ArrayClass) \ - | asArrayModes(ArrayWithContiguous) \ - | asArrayModes(ArrayWithArrayStorage) \ - | asArrayModes(ArrayWithSlowPutArrayStorage)) + | asArrayModes(ArrayWithUndecided) \ + | asArrayModes(ArrayWithInt32) \ + | asArrayModes(ArrayWithDouble) \ + | asArrayModes(ArrayWithContiguous) \ + | asArrayModes(ArrayWithArrayStorage) \ + | asArrayModes(ArrayWithSlowPutArrayStorage)) #define ALL_ARRAY_MODES (ALL_NON_ARRAY_ARRAY_MODES | ALL_ARRAY_ARRAY_MODES) @@ -79,6 +84,36 @@ inline bool arrayModesAlreadyChecked(ArrayModes proven, ArrayModes expected) return (expected | proven) == expected; } +inline bool arrayModesInclude(ArrayModes arrayModes, IndexingType shape) +{ + return !!(arrayModes & (asArrayModes(NonArray | shape) | asArrayModes(ArrayClass | shape))); +} + +inline bool shouldUseSlowPutArrayStorage(ArrayModes arrayModes) +{ + return arrayModesInclude(arrayModes, SlowPutArrayStorageShape); +} + +inline bool shouldUseFastArrayStorage(ArrayModes arrayModes) +{ + return arrayModesInclude(arrayModes, ArrayStorageShape); +} + +inline bool shouldUseContiguous(ArrayModes arrayModes) +{ + return arrayModesInclude(arrayModes, ContiguousShape); +} + +inline bool shouldUseDouble(ArrayModes arrayModes) +{ + return arrayModesInclude(arrayModes, DoubleShape); +} + +inline bool shouldUseInt32(ArrayModes arrayModes) +{ + return arrayModesInclude(arrayModes, Int32Shape); +} + class ArrayProfile { public: ArrayProfile() @@ -128,6 +163,7 @@ public: return !structureIsPolymorphic() && m_expectedStructure; } ArrayModes observedArrayModes() const { return m_observedArrayModes; } + ArrayModes updatedObservedArrayModes() const; // Computes the observed array modes without updating the profile. bool mayInterceptIndexedAccesses() const { return m_mayInterceptIndexedAccesses; } bool mayStoreToHole() const { return m_mayStoreToHole; } diff --git a/Source/JavaScriptCore/bytecode/ByValInfo.h b/Source/JavaScriptCore/bytecode/ByValInfo.h index 8cba4463d..3f79967df 100644 --- a/Source/JavaScriptCore/bytecode/ByValInfo.h +++ b/Source/JavaScriptCore/bytecode/ByValInfo.h @@ -39,6 +39,8 @@ namespace JSC { enum JITArrayMode { + JITInt32, + JITDouble, JITContiguous, JITArrayStorage, JITInt8Array, @@ -55,6 +57,8 @@ enum JITArrayMode { inline bool isOptimizableIndexingType(IndexingType indexingType) { switch (indexingType) { + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: return true; @@ -77,6 +81,10 @@ inline bool hasOptimizableIndexing(Structure* structure) inline JITArrayMode jitArrayModeForIndexingType(IndexingType indexingType) { switch (indexingType) { + case ALL_INT32_INDEXING_TYPES: + return JITInt32; + case ALL_DOUBLE_INDEXING_TYPES: + return JITDouble; case ALL_CONTIGUOUS_INDEXING_TYPES: return JITContiguous; case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: diff --git a/Source/JavaScriptCore/bytecode/CallLinkInfo.cpp b/Source/JavaScriptCore/bytecode/CallLinkInfo.cpp index 4933a494c..762dca12a 100644 --- a/Source/JavaScriptCore/bytecode/CallLinkInfo.cpp +++ b/Source/JavaScriptCore/bytecode/CallLinkInfo.cpp @@ -37,6 +37,7 @@ void CallLinkInfo::unlink(JSGlobalData& globalData, RepatchBuffer& repatchBuffer { ASSERT(isLinked()); + repatchBuffer.revertJumpReplacementToBranchPtrWithPatch(RepatchBuffer::startOfBranchPtrWithPatchOnRegister(hotPathBegin), static_cast<MacroAssembler::RegisterID>(calleeGPR), 0); if (isDFG) { #if ENABLE(DFG_JIT) repatchBuffer.relink(callReturnLocation, (callType == Construct ? globalData.getCTIStub(DFG::linkConstructThunkGenerator) : globalData.getCTIStub(DFG::linkCallThunkGenerator)).code()); @@ -47,6 +48,7 @@ void CallLinkInfo::unlink(JSGlobalData& globalData, RepatchBuffer& repatchBuffer repatchBuffer.relink(callReturnLocation, callType == Construct ? globalData.jitStubs->ctiVirtualConstructLink() : globalData.jitStubs->ctiVirtualCallLink()); hasSeenShouldRepatch = false; callee.clear(); + stub.clear(); // It will be on a list if the callee has a code block. if (isOnList()) diff --git a/Source/JavaScriptCore/bytecode/CallLinkInfo.h b/Source/JavaScriptCore/bytecode/CallLinkInfo.h index 4a78e5d02..89403b0ca 100644 --- a/Source/JavaScriptCore/bytecode/CallLinkInfo.h +++ b/Source/JavaScriptCore/bytecode/CallLinkInfo.h @@ -26,6 +26,7 @@ #ifndef CallLinkInfo_h #define CallLinkInfo_h +#include "ClosureCallStubRoutine.h" #include "CodeLocation.h" #include "JITWriteBarrier.h" #include "JSFunction.h" @@ -70,12 +71,14 @@ struct CallLinkInfo : public BasicRawSentinelNode<CallLinkInfo> { CodeLocationNearCall hotPathOther; JITWriteBarrier<JSFunction> callee; WriteBarrier<JSFunction> lastSeenCallee; + RefPtr<ClosureCallStubRoutine> stub; bool hasSeenShouldRepatch : 1; bool isDFG : 1; CallType callType : 6; - unsigned bytecodeIndex; + unsigned calleeGPR : 8; + CodeOrigin codeOrigin; - bool isLinked() { return callee; } + bool isLinked() { return stub || callee; } void unlink(JSGlobalData&, RepatchBuffer&); bool seenOnce() @@ -96,7 +99,7 @@ inline void* getCallLinkInfoReturnLocation(CallLinkInfo* callLinkInfo) inline unsigned getCallLinkInfoBytecodeIndex(CallLinkInfo* callLinkInfo) { - return callLinkInfo->bytecodeIndex; + return callLinkInfo->codeOrigin.bytecodeIndex; } #endif // ENABLE(JIT) diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp index 5686d5d2c..206d281a2 100644 --- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp +++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp @@ -32,6 +32,7 @@ #include "BytecodeGenerator.h" #include "DFGCapabilities.h" +#include "DFGCommon.h" #include "DFGNode.h" #include "DFGRepatch.h" #include "Debugger.h" @@ -44,7 +45,7 @@ #include "JSValue.h" #include "LowLevelInterpreter.h" #include "RepatchBuffer.h" -#include "SlotVisitorInlineMethods.h" +#include "SlotVisitorInlines.h" #include <stdio.h> #include <wtf/StringExtras.h> #include <wtf/UnusedParam.h> @@ -98,11 +99,11 @@ void CodeBlock::dumpBytecodeCommentAndNewLine(int location) #if ENABLE(BYTECODE_COMMENTS) const char* comment = commentForBytecodeOffset(location); if (comment) - dataLog("\t\t ; %s", comment); + dataLogF("\t\t ; %s", comment); #else UNUSED_PARAM(location); #endif - dataLog("\n"); + dataLogF("\n"); } CString CodeBlock::registerName(ExecState* exec, int r) const @@ -163,33 +164,33 @@ NEVER_INLINE static const char* debugHookName(int debugHookID) return ""; } -void CodeBlock::printUnaryOp(ExecState* exec, int location, Vector<Instruction>::const_iterator& it, const char* op) +void CodeBlock::printUnaryOp(ExecState* exec, int location, const Instruction*& it, const char* op) { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; - dataLog("[%4d] %s\t\t %s, %s", location, op, registerName(exec, r0).data(), registerName(exec, r1).data()); + dataLogF("[%4d] %s\t\t %s, %s", location, op, registerName(exec, r0).data(), registerName(exec, r1).data()); dumpBytecodeCommentAndNewLine(location); } -void CodeBlock::printBinaryOp(ExecState* exec, int location, Vector<Instruction>::const_iterator& it, const char* op) +void CodeBlock::printBinaryOp(ExecState* exec, int location, const Instruction*& it, const char* op) { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; - dataLog("[%4d] %s\t\t %s, %s, %s", location, op, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); + dataLogF("[%4d] %s\t\t %s, %s, %s", location, op, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); dumpBytecodeCommentAndNewLine(location); } -void CodeBlock::printConditionalJump(ExecState* exec, const Vector<Instruction>::const_iterator&, Vector<Instruction>::const_iterator& it, int location, const char* op) +void CodeBlock::printConditionalJump(ExecState* exec, const Instruction*, const Instruction*& it, int location, const char* op) { int r0 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] %s\t\t %s, %d(->%d)", location, op, registerName(exec, r0).data(), offset, location + offset); + dataLogF("[%4d] %s\t\t %s, %d(->%d)", location, op, registerName(exec, r0).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); } -void CodeBlock::printGetByIdOp(ExecState* exec, int location, Vector<Instruction>::const_iterator& it) +void CodeBlock::printGetByIdOp(ExecState* exec, int location, const Instruction*& it) { const char* op; switch (exec->interpreter()->getOpcodeID(it->u.opcode)) { @@ -242,7 +243,7 @@ void CodeBlock::printGetByIdOp(ExecState* exec, int location, Vector<Instruction int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int id0 = (++it)->u.operand; - dataLog("[%4d] %s\t %s, %s, %s", location, op, registerName(exec, r0).data(), registerName(exec, r1).data(), idName(id0, m_identifiers[id0]).data()); + dataLogF("[%4d] %s\t %s, %s, %s", location, op, registerName(exec, r0).data(), registerName(exec, r1).data(), idName(id0, m_identifiers[id0]).data()); it += 5; } @@ -252,18 +253,18 @@ static void dumpStructure(const char* name, ExecState* exec, Structure* structur if (!structure) return; - dataLog("%s = %p", name, structure); + dataLogF("%s = %p", name, structure); PropertyOffset offset = structure->get(exec->globalData(), ident); if (offset != invalidOffset) - dataLog(" (offset = %d)", offset); + dataLogF(" (offset = %d)", offset); } #endif #if ENABLE(JIT) // unused when not ENABLE(JIT), leading to silly warnings static void dumpChain(ExecState* exec, StructureChain* chain, Identifier& ident) { - dataLog("chain = %p: [", chain); + dataLogF("chain = %p: [", chain); bool first = true; for (WriteBarrier<Structure>* currentStructure = chain->head(); *currentStructure; @@ -271,10 +272,10 @@ static void dumpChain(ExecState* exec, StructureChain* chain, Identifier& ident) if (first) first = false; else - dataLog(", "); + dataLogF(", "); dumpStructure("struct", exec, currentStructure->get(), ident); } - dataLog("]"); + dataLogF("]"); } #endif @@ -288,21 +289,21 @@ void CodeBlock::printGetByIdCacheStatus(ExecState* exec, int location) #if ENABLE(LLINT) if (exec->interpreter()->getOpcodeID(instruction[0].u.opcode) == op_get_array_length) - dataLog(" llint(array_length)"); + dataLogF(" llint(array_length)"); else { Structure* structure = instruction[4].u.structure.get(); - dataLog(" llint("); + dataLogF(" llint("); dumpStructure("struct", exec, structure, ident); - dataLog(")"); + dataLogF(")"); } #endif #if ENABLE(JIT) if (numberOfStructureStubInfos()) { - dataLog(" jit("); + dataLogF(" jit("); StructureStubInfo& stubInfo = getStubInfo(location); if (!stubInfo.seen) - dataLog("not seen"); + dataLogF("not seen"); else { Structure* baseStructure = 0; Structure* prototypeStructure = 0; @@ -312,40 +313,40 @@ void CodeBlock::printGetByIdCacheStatus(ExecState* exec, int location) switch (stubInfo.accessType) { case access_get_by_id_self: - dataLog("self"); + dataLogF("self"); baseStructure = stubInfo.u.getByIdSelf.baseObjectStructure.get(); break; case access_get_by_id_proto: - dataLog("proto"); + dataLogF("proto"); baseStructure = stubInfo.u.getByIdProto.baseObjectStructure.get(); prototypeStructure = stubInfo.u.getByIdProto.prototypeStructure.get(); break; case access_get_by_id_chain: - dataLog("chain"); + dataLogF("chain"); baseStructure = stubInfo.u.getByIdChain.baseObjectStructure.get(); chain = stubInfo.u.getByIdChain.chain.get(); break; case access_get_by_id_self_list: - dataLog("self_list"); + dataLogF("self_list"); structureList = stubInfo.u.getByIdSelfList.structureList; listSize = stubInfo.u.getByIdSelfList.listSize; break; case access_get_by_id_proto_list: - dataLog("proto_list"); + dataLogF("proto_list"); structureList = stubInfo.u.getByIdProtoList.structureList; listSize = stubInfo.u.getByIdProtoList.listSize; break; case access_unset: - dataLog("unset"); + dataLogF("unset"); break; case access_get_by_id_generic: - dataLog("generic"); + dataLogF("generic"); break; case access_get_array_length: - dataLog("array_length"); + dataLogF("array_length"); break; case access_get_string_length: - dataLog("string_length"); + dataLogF("string_length"); break; default: ASSERT_NOT_REACHED(); @@ -353,71 +354,71 @@ void CodeBlock::printGetByIdCacheStatus(ExecState* exec, int location) } if (baseStructure) { - dataLog(", "); + dataLogF(", "); dumpStructure("struct", exec, baseStructure, ident); } if (prototypeStructure) { - dataLog(", "); + dataLogF(", "); dumpStructure("prototypeStruct", exec, baseStructure, ident); } if (chain) { - dataLog(", "); + dataLogF(", "); dumpChain(exec, chain, ident); } if (structureList) { - dataLog(", list = %p: [", structureList); + dataLogF(", list = %p: [", structureList); for (int i = 0; i < listSize; ++i) { if (i) - dataLog(", "); - dataLog("("); + dataLogF(", "); + dataLogF("("); dumpStructure("base", exec, structureList->list[i].base.get(), ident); if (structureList->list[i].isChain) { if (structureList->list[i].u.chain.get()) { - dataLog(", "); + dataLogF(", "); dumpChain(exec, structureList->list[i].u.chain.get(), ident); } } else { if (structureList->list[i].u.proto.get()) { - dataLog(", "); + dataLogF(", "); dumpStructure("proto", exec, structureList->list[i].u.proto.get(), ident); } } - dataLog(")"); + dataLogF(")"); } - dataLog("]"); + dataLogF("]"); } } - dataLog(")"); + dataLogF(")"); } #endif } -void CodeBlock::printCallOp(ExecState* exec, int location, Vector<Instruction>::const_iterator& it, const char* op, CacheDumpMode cacheDumpMode) +void CodeBlock::printCallOp(ExecState* exec, int location, const Instruction*& it, const char* op, CacheDumpMode cacheDumpMode) { int func = (++it)->u.operand; int argCount = (++it)->u.operand; int registerOffset = (++it)->u.operand; - dataLog("[%4d] %s\t %s, %d, %d", location, op, registerName(exec, func).data(), argCount, registerOffset); + dataLogF("[%4d] %s\t %s, %d, %d", location, op, registerName(exec, func).data(), argCount, registerOffset); if (cacheDumpMode == DumpCaches) { #if ENABLE(LLINT) LLIntCallLinkInfo* callLinkInfo = it[1].u.callLinkInfo; if (callLinkInfo->lastSeenCallee) { - dataLog(" llint(%p, exec %p)", + dataLogF(" llint(%p, exec %p)", callLinkInfo->lastSeenCallee.get(), callLinkInfo->lastSeenCallee->executable()); } else - dataLog(" llint(not set)"); + dataLogF(" llint(not set)"); #endif #if ENABLE(JIT) if (numberOfCallLinkInfos()) { JSFunction* target = getCallLinkInfo(location).lastSeenCallee.get(); if (target) - dataLog(" jit(%p, exec %p)", target, target->executable()); + dataLogF(" jit(%p, exec %p)", target, target->executable()); else - dataLog(" jit(not set)"); + dataLogF(" jit(not set)"); } #endif } @@ -425,12 +426,12 @@ void CodeBlock::printCallOp(ExecState* exec, int location, Vector<Instruction>:: it += 2; } -void CodeBlock::printPutByIdOp(ExecState* exec, int location, Vector<Instruction>::const_iterator& it, const char* op) +void CodeBlock::printPutByIdOp(ExecState* exec, int location, const Instruction*& it, const char* op) { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int r1 = (++it)->u.operand; - dataLog("[%4d] %s\t %s, %s, %s", location, op, registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data(), registerName(exec, r1).data()); + dataLogF("[%4d] %s\t %s, %s, %s", location, op, registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data(), registerName(exec, r1).data()); dumpBytecodeCommentAndNewLine(location); it += 5; } @@ -438,7 +439,7 @@ void CodeBlock::printPutByIdOp(ExecState* exec, int location, Vector<Instruction void CodeBlock::printStructure(const char* name, const Instruction* vPC, int operand) { unsigned instructionOffset = vPC - instructions().begin(); - dataLog(" [%4d] %s: %s\n", instructionOffset, name, pointerToSourceString(vPC[operand].u.structure).utf8().data()); + dataLogF(" [%4d] %s: %s\n", instructionOffset, name, pointerToSourceString(vPC[operand].u.structure).utf8().data()); } void CodeBlock::printStructures(const Instruction* vPC) @@ -455,15 +456,15 @@ void CodeBlock::printStructures(const Instruction* vPC) return; } if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_proto)) { - dataLog(" [%4d] %s: %s, %s\n", instructionOffset, "get_by_id_proto", pointerToSourceString(vPC[4].u.structure).utf8().data(), pointerToSourceString(vPC[5].u.structure).utf8().data()); + dataLogF(" [%4d] %s: %s, %s\n", instructionOffset, "get_by_id_proto", pointerToSourceString(vPC[4].u.structure).utf8().data(), pointerToSourceString(vPC[5].u.structure).utf8().data()); return; } if (vPC[0].u.opcode == interpreter->getOpcode(op_put_by_id_transition)) { - dataLog(" [%4d] %s: %s, %s, %s\n", instructionOffset, "put_by_id_transition", pointerToSourceString(vPC[4].u.structure).utf8().data(), pointerToSourceString(vPC[5].u.structure).utf8().data(), pointerToSourceString(vPC[6].u.structureChain).utf8().data()); + dataLogF(" [%4d] %s: %s, %s, %s\n", instructionOffset, "put_by_id_transition", pointerToSourceString(vPC[4].u.structure).utf8().data(), pointerToSourceString(vPC[5].u.structure).utf8().data(), pointerToSourceString(vPC[6].u.structureChain).utf8().data()); return; } if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_chain)) { - dataLog(" [%4d] %s: %s, %s\n", instructionOffset, "get_by_id_chain", pointerToSourceString(vPC[4].u.structure).utf8().data(), pointerToSourceString(vPC[5].u.structureChain).utf8().data()); + dataLogF(" [%4d] %s: %s, %s\n", instructionOffset, "get_by_id_chain", pointerToSourceString(vPC[4].u.structure).utf8().data(), pointerToSourceString(vPC[5].u.structureChain).utf8().data()); return; } if (vPC[0].u.opcode == interpreter->getOpcode(op_put_by_id)) { @@ -479,99 +480,103 @@ void CodeBlock::printStructures(const Instruction* vPC) ASSERT(vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_generic) || vPC[0].u.opcode == interpreter->getOpcode(op_put_by_id_generic) || vPC[0].u.opcode == interpreter->getOpcode(op_call) || vPC[0].u.opcode == interpreter->getOpcode(op_call_eval) || vPC[0].u.opcode == interpreter->getOpcode(op_construct)); } -void CodeBlock::dump(ExecState* exec) +void CodeBlock::dump() { + // We only use the ExecState* for things that don't actually lead to JS execution, + // like converting a JSString to a String. Hence the globalExec is appropriate. + ExecState* exec = m_globalObject->globalExec(); + size_t instructionCount = 0; for (size_t i = 0; i < instructions().size(); i += opcodeLengths[exec->interpreter()->getOpcodeID(instructions()[i].u.opcode)]) ++instructionCount; - dataLog( + dataLogF( "%lu m_instructions; %lu bytes at %p (%s); %d parameter(s); %d callee register(s); %d variable(s)", static_cast<unsigned long>(instructions().size()), static_cast<unsigned long>(instructions().size() * sizeof(Instruction)), this, codeTypeToString(codeType()), m_numParameters, m_numCalleeRegisters, m_numVars); if (symbolTable()->captureCount()) - dataLog("; %d captured var(s)", symbolTable()->captureCount()); + dataLogF("; %d captured var(s)", symbolTable()->captureCount()); if (usesArguments()) { - dataLog( + dataLogF( "; uses arguments, in r%d, r%d", argumentsRegister(), unmodifiedArgumentsRegister(argumentsRegister())); } if (needsFullScopeChain() && codeType() == FunctionCode) - dataLog("; activation in r%d", activationRegister()); - dataLog("\n\n"); + dataLogF("; activation in r%d", activationRegister()); + dataLogF("\n\n"); - Vector<Instruction>::const_iterator begin = instructions().begin(); - Vector<Instruction>::const_iterator end = instructions().end(); - for (Vector<Instruction>::const_iterator it = begin; it != end; ++it) + const Instruction* begin = instructions().begin(); + const Instruction* end = instructions().end(); + for (const Instruction* it = begin; it != end; ++it) dump(exec, begin, it); if (!m_identifiers.isEmpty()) { - dataLog("\nIdentifiers:\n"); + dataLogF("\nIdentifiers:\n"); size_t i = 0; do { - dataLog(" id%u = %s\n", static_cast<unsigned>(i), m_identifiers[i].string().utf8().data()); + dataLogF(" id%u = %s\n", static_cast<unsigned>(i), m_identifiers[i].string().utf8().data()); ++i; } while (i != m_identifiers.size()); } if (!m_constantRegisters.isEmpty()) { - dataLog("\nConstants:\n"); + dataLogF("\nConstants:\n"); size_t i = 0; do { - dataLog(" k%u = %s\n", static_cast<unsigned>(i), valueToSourceString(exec, m_constantRegisters[i].get()).utf8().data()); + dataLogF(" k%u = %s\n", static_cast<unsigned>(i), valueToSourceString(exec, m_constantRegisters[i].get()).utf8().data()); ++i; } while (i < m_constantRegisters.size()); } if (size_t count = m_unlinkedCode->numberOfRegExps()) { - dataLog("\nm_regexps:\n"); + dataLogF("\nm_regexps:\n"); size_t i = 0; do { - dataLog(" re%u = %s\n", static_cast<unsigned>(i), regexpToSourceString(m_unlinkedCode->regexp(i)).utf8().data()); + dataLogF(" re%u = %s\n", static_cast<unsigned>(i), regexpToSourceString(m_unlinkedCode->regexp(i)).utf8().data()); ++i; } while (i < count); } #if ENABLE(JIT) if (!m_structureStubInfos.isEmpty()) - dataLog("\nStructures:\n"); + dataLogF("\nStructures:\n"); #endif if (m_rareData && !m_rareData->m_exceptionHandlers.isEmpty()) { - dataLog("\nException Handlers:\n"); + dataLogF("\nException Handlers:\n"); unsigned i = 0; do { - dataLog("\t %d: { start: [%4d] end: [%4d] target: [%4d] }\n", i + 1, m_rareData->m_exceptionHandlers[i].start, m_rareData->m_exceptionHandlers[i].end, m_rareData->m_exceptionHandlers[i].target); + dataLogF("\t %d: { start: [%4d] end: [%4d] target: [%4d] }\n", i + 1, m_rareData->m_exceptionHandlers[i].start, m_rareData->m_exceptionHandlers[i].end, m_rareData->m_exceptionHandlers[i].target); ++i; } while (i < m_rareData->m_exceptionHandlers.size()); } if (m_rareData && !m_rareData->m_immediateSwitchJumpTables.isEmpty()) { - dataLog("Immediate Switch Jump Tables:\n"); + dataLogF("Immediate Switch Jump Tables:\n"); unsigned i = 0; do { - dataLog(" %1d = {\n", i); + dataLogF(" %1d = {\n", i); int entry = 0; Vector<int32_t>::const_iterator end = m_rareData->m_immediateSwitchJumpTables[i].branchOffsets.end(); for (Vector<int32_t>::const_iterator iter = m_rareData->m_immediateSwitchJumpTables[i].branchOffsets.begin(); iter != end; ++iter, ++entry) { if (!*iter) continue; - dataLog("\t\t%4d => %04d\n", entry + m_rareData->m_immediateSwitchJumpTables[i].min, *iter); + dataLogF("\t\t%4d => %04d\n", entry + m_rareData->m_immediateSwitchJumpTables[i].min, *iter); } - dataLog(" }\n"); + dataLogF(" }\n"); ++i; } while (i < m_rareData->m_immediateSwitchJumpTables.size()); } if (m_rareData && !m_rareData->m_characterSwitchJumpTables.isEmpty()) { - dataLog("\nCharacter Switch Jump Tables:\n"); + dataLogF("\nCharacter Switch Jump Tables:\n"); unsigned i = 0; do { - dataLog(" %1d = {\n", i); + dataLogF(" %1d = {\n", i); int entry = 0; Vector<int32_t>::const_iterator end = m_rareData->m_characterSwitchJumpTables[i].branchOffsets.end(); for (Vector<int32_t>::const_iterator iter = m_rareData->m_characterSwitchJumpTables[i].branchOffsets.begin(); iter != end; ++iter, ++entry) { @@ -579,72 +584,79 @@ void CodeBlock::dump(ExecState* exec) continue; ASSERT(!((i + m_rareData->m_characterSwitchJumpTables[i].min) & ~0xFFFF)); UChar ch = static_cast<UChar>(entry + m_rareData->m_characterSwitchJumpTables[i].min); - dataLog("\t\t\"%s\" => %04d\n", String(&ch, 1).utf8().data(), *iter); + dataLogF("\t\t\"%s\" => %04d\n", String(&ch, 1).utf8().data(), *iter); } - dataLog(" }\n"); + dataLogF(" }\n"); ++i; } while (i < m_rareData->m_characterSwitchJumpTables.size()); } if (m_rareData && !m_rareData->m_stringSwitchJumpTables.isEmpty()) { - dataLog("\nString Switch Jump Tables:\n"); + dataLogF("\nString Switch Jump Tables:\n"); unsigned i = 0; do { - dataLog(" %1d = {\n", i); + dataLogF(" %1d = {\n", i); StringJumpTable::StringOffsetTable::const_iterator end = m_rareData->m_stringSwitchJumpTables[i].offsetTable.end(); for (StringJumpTable::StringOffsetTable::const_iterator iter = m_rareData->m_stringSwitchJumpTables[i].offsetTable.begin(); iter != end; ++iter) - dataLog("\t\t\"%s\" => %04d\n", String(iter->key).utf8().data(), iter->value.branchOffset); - dataLog(" }\n"); + dataLogF("\t\t\"%s\" => %04d\n", String(iter->key).utf8().data(), iter->value.branchOffset); + dataLogF(" }\n"); ++i; } while (i < m_rareData->m_stringSwitchJumpTables.size()); } - dataLog("\n"); + dataLogF("\n"); } -void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& begin, Vector<Instruction>::const_iterator& it) +void CodeBlock::dump(ExecState* exec, const Instruction* begin, const Instruction*& it) { int location = it - begin; switch (exec->interpreter()->getOpcodeID(it->u.opcode)) { case op_enter: { - dataLog("[%4d] enter", location); + dataLogF("[%4d] enter", location); dumpBytecodeCommentAndNewLine(location); break; } case op_create_activation: { int r0 = (++it)->u.operand; - dataLog("[%4d] create_activation %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] create_activation %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_create_arguments: { int r0 = (++it)->u.operand; - dataLog("[%4d] create_arguments\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] create_arguments\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_init_lazy_reg: { int r0 = (++it)->u.operand; - dataLog("[%4d] init_lazy_reg\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] init_lazy_reg\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); break; } + case op_get_callee: { + int r0 = (++it)->u.operand; + dataLogF("[%4d] op_get_callee %s\n", location, registerName(exec, r0).data()); + ++it; + break; + } case op_create_this: { int r0 = (++it)->u.operand; - dataLog("[%4d] create_this %s", location, registerName(exec, r0).data()); + int r1 = (++it)->u.operand; + dataLogF("[%4d] create_this %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_convert_this: { int r0 = (++it)->u.operand; - dataLog("[%4d] convert_this\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] convert_this\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); ++it; // Skip value profile. break; } case op_new_object: { int r0 = (++it)->u.operand; - dataLog("[%4d] new_object\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] new_object\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -652,40 +664,43 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int dst = (++it)->u.operand; int argv = (++it)->u.operand; int argc = (++it)->u.operand; - dataLog("[%4d] new_array\t %s, %s, %d", location, registerName(exec, dst).data(), registerName(exec, argv).data(), argc); + dataLogF("[%4d] new_array\t %s, %s, %d", location, registerName(exec, dst).data(), registerName(exec, argv).data(), argc); dumpBytecodeCommentAndNewLine(location); + ++it; // Skip array allocation profile. break; } case op_new_array_with_size: { int dst = (++it)->u.operand; int length = (++it)->u.operand; - dataLog("[%4d] new_array_with_size\t %s, %s", location, registerName(exec, dst).data(), registerName(exec, length).data()); + dataLogF("[%4d] new_array_with_size\t %s, %s", location, registerName(exec, dst).data(), registerName(exec, length).data()); dumpBytecodeCommentAndNewLine(location); + ++it; // Skip array allocation profile. break; } case op_new_array_buffer: { int dst = (++it)->u.operand; int argv = (++it)->u.operand; int argc = (++it)->u.operand; - dataLog("[%4d] new_array_buffer\t %s, %d, %d", location, registerName(exec, dst).data(), argv, argc); + dataLogF("[%4d] new_array_buffer\t %s, %d, %d", location, registerName(exec, dst).data(), argv, argc); dumpBytecodeCommentAndNewLine(location); + ++it; // Skip array allocation profile. break; } case op_new_regexp: { int r0 = (++it)->u.operand; int re0 = (++it)->u.operand; - dataLog("[%4d] new_regexp\t %s, ", location, registerName(exec, r0).data()); + dataLogF("[%4d] new_regexp\t %s, ", location, registerName(exec, r0).data()); if (r0 >=0 && r0 < (int)m_unlinkedCode->numberOfRegExps()) - dataLog("%s", regexpName(re0, regexp(re0)).data()); + dataLogF("%s", regexpName(re0, regexp(re0)).data()); else - dataLog("bad_regexp(%d)", re0); + dataLogF("bad_regexp(%d)", re0); dumpBytecodeCommentAndNewLine(location); break; } case op_mov: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; - dataLog("[%4d] mov\t\t %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data()); + dataLogF("[%4d] mov\t\t %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -735,13 +750,13 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& } case op_pre_inc: { int r0 = (++it)->u.operand; - dataLog("[%4d] pre_inc\t\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] pre_inc\t\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_pre_dec: { int r0 = (++it)->u.operand; - dataLog("[%4d] pre_dec\t\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] pre_dec\t\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -817,7 +832,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] check_has_instance\t\t %s, %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data(), offset, location + offset); + dataLogF("[%4d] check_has_instance\t\t %s, %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -825,7 +840,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; - dataLog("[%4d] instanceof\t\t %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); + dataLogF("[%4d] instanceof\t\t %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -867,7 +882,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int id0 = (++it)->u.operand; int value = (++it)->u.operand; int resolveInfo = (++it)->u.operand; - dataLog("[%4d] put_to_base\t %s, %s, %s, %d", location, registerName(exec, base).data(), idName(id0, m_identifiers[id0]).data(), registerName(exec, value).data(), resolveInfo); + dataLogF("[%4d] put_to_base\t %s, %s, %s, %d", location, registerName(exec, base).data(), idName(id0, m_identifiers[id0]).data(), registerName(exec, value).data(), resolveInfo); dumpBytecodeCommentAndNewLine(location); break; } @@ -880,13 +895,13 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int resolveInfo = (++it)->u.operand; - dataLog("[%4d] resolve\t\t %s, %s, %d", location, registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data(), resolveInfo); + dataLogF("[%4d] resolve\t\t %s, %s, %d", location, registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data(), resolveInfo); dumpBytecodeCommentAndNewLine(location); it++; break; } case op_init_global_const_nop: { - dataLog("[%4d] init_global_const_nop\t", location); + dataLogF("[%4d] init_global_const_nop\t", location); dumpBytecodeCommentAndNewLine(location); it++; it++; @@ -897,7 +912,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& case op_init_global_const: { WriteBarrier<Unknown>* registerPointer = (++it)->u.registerPointer; int r0 = (++it)->u.operand; - dataLog("[%4d] init_global_const\t g%d(%p), %s", location, m_globalObject->findRegisterIndex(registerPointer), registerPointer, registerName(exec, r0).data()); + dataLogF("[%4d] init_global_const\t g%d(%p), %s", location, m_globalObject->findRegisterIndex(registerPointer), registerPointer, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); it++; it++; @@ -906,7 +921,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& case op_init_global_const_check: { WriteBarrier<Unknown>* registerPointer = (++it)->u.registerPointer; int r0 = (++it)->u.operand; - dataLog("[%4d] init_global_const_check\t g%d(%p), %s", location, m_globalObject->findRegisterIndex(registerPointer), registerPointer, registerName(exec, r0).data()); + dataLogF("[%4d] init_global_const_check\t g%d(%p), %s", location, m_globalObject->findRegisterIndex(registerPointer), registerPointer, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); it++; it++; @@ -922,7 +937,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int isStrict = (++it)->u.operand; int resolveInfo = (++it)->u.operand; int putToBaseInfo = (++it)->u.operand; - dataLog("[%4d] resolve_base%s\t %s, %s, %d, %d", location, isStrict ? "_strict" : "", registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data(), resolveInfo, putToBaseInfo); + dataLogF("[%4d] resolve_base%s\t %s, %s, %d, %d", location, isStrict ? "_strict" : "", registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data(), resolveInfo, putToBaseInfo); dumpBytecodeCommentAndNewLine(location); it++; break; @@ -930,7 +945,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& case op_ensure_property_exists: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; - dataLog("[%4d] ensure_property_exists\t %s, %s", location, registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data()); + dataLogF("[%4d] ensure_property_exists\t %s, %s", location, registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -940,7 +955,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int id0 = (++it)->u.operand; int resolveInfo = (++it)->u.operand; int putToBaseInfo = (++it)->u.operand; - dataLog("[%4d] resolve_with_base %s, %s, %s, %d, %d", location, registerName(exec, r0).data(), registerName(exec, r1).data(), idName(id0, m_identifiers[id0]).data(), resolveInfo, putToBaseInfo); + dataLogF("[%4d] resolve_with_base %s, %s, %s, %d, %d", location, registerName(exec, r0).data(), registerName(exec, r1).data(), idName(id0, m_identifiers[id0]).data(), resolveInfo, putToBaseInfo); dumpBytecodeCommentAndNewLine(location); it++; break; @@ -950,7 +965,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r1 = (++it)->u.operand; int id0 = (++it)->u.operand; int resolveInfo = (++it)->u.operand; - dataLog("[%4d] resolve_with_this %s, %s, %s, %d", location, registerName(exec, r0).data(), registerName(exec, r1).data(), idName(id0, m_identifiers[id0]).data(), resolveInfo); + dataLogF("[%4d] resolve_with_this %s, %s, %s, %d", location, registerName(exec, r0).data(), registerName(exec, r1).data(), idName(id0, m_identifiers[id0]).data(), resolveInfo); dumpBytecodeCommentAndNewLine(location); it++; break; @@ -1020,7 +1035,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int id0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; - dataLog("[%4d] put_getter_setter\t %s, %s, %s, %s", location, registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); + dataLogF("[%4d] put_getter_setter\t %s, %s, %s, %s", location, registerName(exec, r0).data(), idName(id0, m_identifiers[id0]).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -1028,7 +1043,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int id0 = (++it)->u.operand; - dataLog("[%4d] del_by_id\t %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), idName(id0, m_identifiers[id0]).data()); + dataLogF("[%4d] del_by_id\t %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), idName(id0, m_identifiers[id0]).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -1036,7 +1051,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; - dataLog("[%4d] get_by_val\t %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); + dataLogF("[%4d] get_by_val\t %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); dumpBytecodeCommentAndNewLine(location); it++; it++; @@ -1046,7 +1061,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; - dataLog("[%4d] get_argument_by_val\t %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); + dataLogF("[%4d] get_argument_by_val\t %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); dumpBytecodeCommentAndNewLine(location); ++it; ++it; @@ -1059,7 +1074,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r3 = (++it)->u.operand; int r4 = (++it)->u.operand; int r5 = (++it)->u.operand; - dataLog("[%4d] get_by_pname\t %s, %s, %s, %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data(), registerName(exec, r3).data(), registerName(exec, r4).data(), registerName(exec, r5).data()); + dataLogF("[%4d] get_by_pname\t %s, %s, %s, %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data(), registerName(exec, r3).data(), registerName(exec, r4).data(), registerName(exec, r5).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -1067,7 +1082,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; - dataLog("[%4d] put_by_val\t %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); + dataLogF("[%4d] put_by_val\t %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); dumpBytecodeCommentAndNewLine(location); ++it; break; @@ -1076,7 +1091,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; - dataLog("[%4d] del_by_val\t %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); + dataLogF("[%4d] del_by_val\t %s, %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -1084,19 +1099,19 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; unsigned n0 = (++it)->u.operand; int r1 = (++it)->u.operand; - dataLog("[%4d] put_by_index\t %s, %u, %s", location, registerName(exec, r0).data(), n0, registerName(exec, r1).data()); + dataLogF("[%4d] put_by_index\t %s, %u, %s", location, registerName(exec, r0).data(), n0, registerName(exec, r1).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_jmp: { int offset = (++it)->u.operand; - dataLog("[%4d] jmp\t\t %d(->%d)", location, offset, location + offset); + dataLogF("[%4d] jmp\t\t %d(->%d)", location, offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } case op_loop: { int offset = (++it)->u.operand; - dataLog("[%4d] loop\t\t %d(->%d)", location, offset, location + offset); + dataLogF("[%4d] loop\t\t %d(->%d)", location, offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1128,7 +1143,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; Special::Pointer pointer = (++it)->u.specialPointer; int offset = (++it)->u.operand; - dataLog("[%4d] jneq_ptr\t\t %s, %d (%p), %d(->%d)", location, registerName(exec, r0).data(), pointer, m_globalObject->actualPointerFor(pointer), offset, location + offset); + dataLogF("[%4d] jneq_ptr\t\t %s, %d (%p), %d(->%d)", location, registerName(exec, r0).data(), pointer, m_globalObject->actualPointerFor(pointer), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1136,7 +1151,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] jless\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); + dataLogF("[%4d] jless\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1144,7 +1159,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] jlesseq\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); + dataLogF("[%4d] jlesseq\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1152,7 +1167,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] jgreater\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); + dataLogF("[%4d] jgreater\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1160,7 +1175,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] jgreatereq\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); + dataLogF("[%4d] jgreatereq\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1168,7 +1183,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] jnless\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); + dataLogF("[%4d] jnless\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1176,7 +1191,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] jnlesseq\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); + dataLogF("[%4d] jnlesseq\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1184,7 +1199,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] jngreater\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); + dataLogF("[%4d] jngreater\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1192,7 +1207,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] jngreatereq\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); + dataLogF("[%4d] jngreatereq\t\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1200,7 +1215,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] loop_if_less\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); + dataLogF("[%4d] loop_if_less\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1208,7 +1223,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] loop_if_lesseq\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); + dataLogF("[%4d] loop_if_lesseq\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1216,7 +1231,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] loop_if_greater\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); + dataLogF("[%4d] loop_if_greater\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } @@ -1224,12 +1239,12 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] loop_if_greatereq\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); + dataLogF("[%4d] loop_if_greatereq\t %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } case op_loop_hint: { - dataLog("[%4d] loop_hint", location); + dataLogF("[%4d] loop_hint", location); dumpBytecodeCommentAndNewLine(location); break; } @@ -1237,7 +1252,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int tableIndex = (++it)->u.operand; int defaultTarget = (++it)->u.operand; int scrutineeRegister = (++it)->u.operand; - dataLog("[%4d] switch_imm\t %d, %d(->%d), %s", location, tableIndex, defaultTarget, location + defaultTarget, registerName(exec, scrutineeRegister).data()); + dataLogF("[%4d] switch_imm\t %d, %d(->%d), %s", location, tableIndex, defaultTarget, location + defaultTarget, registerName(exec, scrutineeRegister).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -1245,7 +1260,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int tableIndex = (++it)->u.operand; int defaultTarget = (++it)->u.operand; int scrutineeRegister = (++it)->u.operand; - dataLog("[%4d] switch_char\t %d, %d(->%d), %s", location, tableIndex, defaultTarget, location + defaultTarget, registerName(exec, scrutineeRegister).data()); + dataLogF("[%4d] switch_char\t %d, %d(->%d), %s", location, tableIndex, defaultTarget, location + defaultTarget, registerName(exec, scrutineeRegister).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -1253,7 +1268,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int tableIndex = (++it)->u.operand; int defaultTarget = (++it)->u.operand; int scrutineeRegister = (++it)->u.operand; - dataLog("[%4d] switch_string\t %d, %d(->%d), %s", location, tableIndex, defaultTarget, location + defaultTarget, registerName(exec, scrutineeRegister).data()); + dataLogF("[%4d] switch_string\t %d, %d(->%d), %s", location, tableIndex, defaultTarget, location + defaultTarget, registerName(exec, scrutineeRegister).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -1261,14 +1276,14 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int f0 = (++it)->u.operand; int shouldCheck = (++it)->u.operand; - dataLog("[%4d] new_func\t\t %s, f%d, %s", location, registerName(exec, r0).data(), f0, shouldCheck ? "<Checked>" : "<Unchecked>"); + dataLogF("[%4d] new_func\t\t %s, f%d, %s", location, registerName(exec, r0).data(), f0, shouldCheck ? "<Checked>" : "<Unchecked>"); dumpBytecodeCommentAndNewLine(location); break; } case op_new_func_exp: { int r0 = (++it)->u.operand; int f0 = (++it)->u.operand; - dataLog("[%4d] new_func_exp\t %s, f%d", location, registerName(exec, r0).data(), f0); + dataLogF("[%4d] new_func_exp\t %s, f%d", location, registerName(exec, r0).data(), f0); dumpBytecodeCommentAndNewLine(location); break; } @@ -1285,32 +1300,32 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int thisValue = (++it)->u.operand; int arguments = (++it)->u.operand; int firstFreeRegister = (++it)->u.operand; - dataLog("[%4d] call_varargs\t %s, %s, %s, %d", location, registerName(exec, callee).data(), registerName(exec, thisValue).data(), registerName(exec, arguments).data(), firstFreeRegister); + dataLogF("[%4d] call_varargs\t %s, %s, %s, %d", location, registerName(exec, callee).data(), registerName(exec, thisValue).data(), registerName(exec, arguments).data(), firstFreeRegister); dumpBytecodeCommentAndNewLine(location); break; } case op_tear_off_activation: { int r0 = (++it)->u.operand; - dataLog("[%4d] tear_off_activation\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] tear_off_activation\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_tear_off_arguments: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; - dataLog("[%4d] tear_off_arguments %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data()); + dataLogF("[%4d] tear_off_arguments %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_ret: { int r0 = (++it)->u.operand; - dataLog("[%4d] ret\t\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] ret\t\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_call_put_result: { int r0 = (++it)->u.operand; - dataLog("[%4d] call_put_result\t\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] call_put_result\t\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); it++; break; @@ -1318,7 +1333,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& case op_ret_object_or_this: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; - dataLog("[%4d] constructor_ret\t\t %s %s", location, registerName(exec, r0).data(), registerName(exec, r1).data()); + dataLogF("[%4d] constructor_ret\t\t %s %s", location, registerName(exec, r0).data(), registerName(exec, r1).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -1330,14 +1345,14 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int count = (++it)->u.operand; - dataLog("[%4d] strcat\t\t %s, %s, %d", location, registerName(exec, r0).data(), registerName(exec, r1).data(), count); + dataLogF("[%4d] strcat\t\t %s, %s, %d", location, registerName(exec, r0).data(), registerName(exec, r1).data(), count); dumpBytecodeCommentAndNewLine(location); break; } case op_to_primitive: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; - dataLog("[%4d] to_primitive\t %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data()); + dataLogF("[%4d] to_primitive\t %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -1347,7 +1362,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r2 = it[3].u.operand; int r3 = it[4].u.operand; int offset = it[5].u.operand; - dataLog("[%4d] get_pnames\t %s, %s, %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data(), registerName(exec, r3).data(), offset, location + offset); + dataLogF("[%4d] get_pnames\t %s, %s, %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data(), registerName(exec, r3).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); it += OPCODE_LENGTH(op_get_pnames) - 1; break; @@ -1359,19 +1374,19 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int size = it[4].u.operand; int iter = it[5].u.operand; int offset = it[6].u.operand; - dataLog("[%4d] next_pname\t %s, %s, %s, %s, %s, %d(->%d)", location, registerName(exec, dest).data(), registerName(exec, base).data(), registerName(exec, i).data(), registerName(exec, size).data(), registerName(exec, iter).data(), offset, location + offset); + dataLogF("[%4d] next_pname\t %s, %s, %s, %s, %s, %d(->%d)", location, registerName(exec, dest).data(), registerName(exec, base).data(), registerName(exec, i).data(), registerName(exec, size).data(), registerName(exec, iter).data(), offset, location + offset); dumpBytecodeCommentAndNewLine(location); it += OPCODE_LENGTH(op_next_pname) - 1; break; } case op_push_with_scope: { int r0 = (++it)->u.operand; - dataLog("[%4d] push_with_scope\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] push_with_scope\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_pop_scope: { - dataLog("[%4d] pop_scope", location); + dataLogF("[%4d] pop_scope", location); dumpBytecodeCommentAndNewLine(location); break; } @@ -1379,33 +1394,33 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int id0 = (++it)->u.operand; int r1 = (++it)->u.operand; unsigned attributes = (++it)->u.operand; - dataLog("[%4d] push_name_scope \t%s, %s, %u", location, idName(id0, m_identifiers[id0]).data(), registerName(exec, r1).data(), attributes); + dataLogF("[%4d] push_name_scope \t%s, %s, %u", location, idName(id0, m_identifiers[id0]).data(), registerName(exec, r1).data(), attributes); dumpBytecodeCommentAndNewLine(location); break; } case op_jmp_scopes: { int scopeDelta = (++it)->u.operand; int offset = (++it)->u.operand; - dataLog("[%4d] jmp_scopes\t^%d, %d(->%d)", location, scopeDelta, offset, location + offset); + dataLogF("[%4d] jmp_scopes\t^%d, %d(->%d)", location, scopeDelta, offset, location + offset); dumpBytecodeCommentAndNewLine(location); break; } case op_catch: { int r0 = (++it)->u.operand; - dataLog("[%4d] catch\t\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] catch\t\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_throw: { int r0 = (++it)->u.operand; - dataLog("[%4d] throw\t\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] throw\t\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_throw_static_error: { int k0 = (++it)->u.operand; int k1 = (++it)->u.operand; - dataLog("[%4d] throw_static_error\t %s, %s", location, constantName(exec, k0, getConstant(k0)).data(), k1 ? "true" : "false"); + dataLogF("[%4d] throw_static_error\t %s, %s", location, constantName(exec, k0, getConstant(k0)).data(), k1 ? "true" : "false"); dumpBytecodeCommentAndNewLine(location); break; } @@ -1414,25 +1429,25 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int firstLine = (++it)->u.operand; int lastLine = (++it)->u.operand; int column = (++it)->u.operand; - dataLog("[%4d] debug\t\t %s, %d, %d, %d", location, debugHookName(debugHookID), firstLine, lastLine, column); + dataLogF("[%4d] debug\t\t %s, %d, %d, %d", location, debugHookName(debugHookID), firstLine, lastLine, column); dumpBytecodeCommentAndNewLine(location); break; } case op_profile_will_call: { int function = (++it)->u.operand; - dataLog("[%4d] profile_will_call %s", location, registerName(exec, function).data()); + dataLogF("[%4d] profile_will_call %s", location, registerName(exec, function).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_profile_did_call: { int function = (++it)->u.operand; - dataLog("[%4d] profile_did_call\t %s", location, registerName(exec, function).data()); + dataLogF("[%4d] profile_did_call\t %s", location, registerName(exec, function).data()); dumpBytecodeCommentAndNewLine(location); break; } case op_end: { int r0 = (++it)->u.operand; - dataLog("[%4d] end\t\t %s", location, registerName(exec, r0).data()); + dataLogF("[%4d] end\t\t %s", location, registerName(exec, r0).data()); dumpBytecodeCommentAndNewLine(location); break; } @@ -1443,6 +1458,13 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& } } +void CodeBlock::dump(unsigned bytecodeOffset) +{ + ExecState* exec = m_globalObject->globalExec(); + const Instruction* it = instructions().begin() + bytecodeOffset; + dump(exec, instructions().begin(), it); +} + #if DUMP_CODE_BLOCK_STATISTICS static HashSet<CodeBlock*> liveCodeBlockSet; #endif @@ -1541,29 +1563,29 @@ void CodeBlock::dumpStatistics() totalSize += symbolTableTotalSize; totalSize += (liveCodeBlockSet.size() * sizeof(CodeBlock)); - dataLog("Number of live CodeBlocks: %d\n", liveCodeBlockSet.size()); - dataLog("Size of a single CodeBlock [sizeof(CodeBlock)]: %zu\n", sizeof(CodeBlock)); - dataLog("Size of all CodeBlocks: %zu\n", totalSize); - dataLog("Average size of a CodeBlock: %zu\n", totalSize / liveCodeBlockSet.size()); + dataLogF("Number of live CodeBlocks: %d\n", liveCodeBlockSet.size()); + dataLogF("Size of a single CodeBlock [sizeof(CodeBlock)]: %zu\n", sizeof(CodeBlock)); + dataLogF("Size of all CodeBlocks: %zu\n", totalSize); + dataLogF("Average size of a CodeBlock: %zu\n", totalSize / liveCodeBlockSet.size()); - dataLog("Number of FunctionCode CodeBlocks: %zu (%.3f%%)\n", isFunctionCode, static_cast<double>(isFunctionCode) * 100.0 / liveCodeBlockSet.size()); - dataLog("Number of GlobalCode CodeBlocks: %zu (%.3f%%)\n", isGlobalCode, static_cast<double>(isGlobalCode) * 100.0 / liveCodeBlockSet.size()); - dataLog("Number of EvalCode CodeBlocks: %zu (%.3f%%)\n", isEvalCode, static_cast<double>(isEvalCode) * 100.0 / liveCodeBlockSet.size()); + dataLogF("Number of FunctionCode CodeBlocks: %zu (%.3f%%)\n", isFunctionCode, static_cast<double>(isFunctionCode) * 100.0 / liveCodeBlockSet.size()); + dataLogF("Number of GlobalCode CodeBlocks: %zu (%.3f%%)\n", isGlobalCode, static_cast<double>(isGlobalCode) * 100.0 / liveCodeBlockSet.size()); + dataLogF("Number of EvalCode CodeBlocks: %zu (%.3f%%)\n", isEvalCode, static_cast<double>(isEvalCode) * 100.0 / liveCodeBlockSet.size()); - dataLog("Number of CodeBlocks with rare data: %zu (%.3f%%)\n", hasRareData, static_cast<double>(hasRareData) * 100.0 / liveCodeBlockSet.size()); + dataLogF("Number of CodeBlocks with rare data: %zu (%.3f%%)\n", hasRareData, static_cast<double>(hasRareData) * 100.0 / liveCodeBlockSet.size()); - #define PRINT_STATS(name) dataLog("Number of CodeBlocks with " #name ": %zu\n", name##IsNotEmpty); dataLog("Size of all " #name ": %zu\n", name##TotalSize); + #define PRINT_STATS(name) dataLogF("Number of CodeBlocks with " #name ": %zu\n", name##IsNotEmpty); dataLogF("Size of all " #name ": %zu\n", name##TotalSize); FOR_EACH_MEMBER_VECTOR(PRINT_STATS) FOR_EACH_MEMBER_VECTOR_RARE_DATA(PRINT_STATS) #undef PRINT_STATS - dataLog("Number of CodeBlocks with evalCodeCache: %zu\n", evalCodeCacheIsNotEmpty); - dataLog("Number of CodeBlocks with symbolTable: %zu\n", symbolTableIsNotEmpty); + dataLogF("Number of CodeBlocks with evalCodeCache: %zu\n", evalCodeCacheIsNotEmpty); + dataLogF("Number of CodeBlocks with symbolTable: %zu\n", symbolTableIsNotEmpty); - dataLog("Size of all symbolTables: %zu\n", symbolTableTotalSize); + dataLogF("Size of all symbolTables: %zu\n", symbolTableTotalSize); #else - dataLog("Dumping CodeBlock statistics is not enabled.\n"); + dataLogF("Dumping CodeBlock statistics is not enabled.\n"); #endif } @@ -1746,6 +1768,8 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin #if ENABLE(DFG_JIT) if (size_t size = unlinkedCodeBlock->numberOfArrayProfiles()) m_arrayProfiles.grow(size); + if (size_t size = unlinkedCodeBlock->numberOfArrayAllocationProfiles()) + m_arrayAllocationProfiles.grow(size); if (size_t size = unlinkedCodeBlock->numberOfValueProfiles()) m_valueProfiles.grow(size); #endif @@ -1786,7 +1810,8 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin case op_resolve_with_base: case op_resolve_with_this: case op_get_by_id: - case op_call_put_result: { + case op_call_put_result: + case op_get_callee: { ValueProfile* profile = &m_valueProfiles[pc[i + opLength - 1].u.operand]; ASSERT(profile->m_bytecodeOffset == -1); profile->m_bytecodeOffset = i; @@ -1800,22 +1825,32 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin break; } + case op_new_array: + case op_new_array_buffer: + case op_new_array_with_size: { + int arrayAllocationProfileIndex = pc[i + opLength - 1].u.operand; + instructions[i + opLength - 1] = &m_arrayAllocationProfiles[arrayAllocationProfileIndex]; + break; + } +#endif + case op_call: case op_call_eval: { +#if ENABLE(DFG_JIT) int arrayProfileIndex = pc[i + opLength - 1].u.operand; m_arrayProfiles[arrayProfileIndex] = ArrayProfile(i); instructions[i + opLength - 1] = &m_arrayProfiles[arrayProfileIndex]; - // fallthrough -#if !ENABLE(LLINT) - break; -#endif - } #endif #if ENABLE(LLINT) - case op_construct: instructions[i + 4] = &m_llintCallLinkInfos[pc[i + 4].u.operand]; +#endif break; + } + case op_construct: +#if ENABLE(LLINT) + instructions[i + 4] = &m_llintCallLinkInfos[pc[i + 4].u.operand]; #endif + break; case op_get_by_id_out_of_line: case op_get_by_id_self: case op_get_by_id_proto: @@ -1857,7 +1892,7 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin m_instructions = WTF::RefCountedArray<Instruction>(instructions); if (BytecodeGenerator::dumpsGeneratedCode()) - dump(m_globalObject->globalExec()); + dump(); m_globalData->finishedCompiling(this); } @@ -2118,7 +2153,7 @@ void CodeBlock::finalizeUnconditionally() if (!curInstruction[4].u.structure || Heap::isMarked(curInstruction[4].u.structure.get())) break; if (verboseUnlinking) - dataLog("Clearing LLInt property access with structure %p.\n", curInstruction[4].u.structure.get()); + dataLogF("Clearing LLInt property access with structure %p.\n", curInstruction[4].u.structure.get()); curInstruction[4].u.structure.clear(); curInstruction[5].u.operand = 0; break; @@ -2131,7 +2166,7 @@ void CodeBlock::finalizeUnconditionally() && Heap::isMarked(curInstruction[7].u.structureChain.get())) break; if (verboseUnlinking) { - dataLog("Clearing LLInt put transition with structures %p -> %p, chain %p.\n", + dataLogF("Clearing LLInt put transition with structures %p -> %p, chain %p.\n", curInstruction[4].u.structure.get(), curInstruction[6].u.structure.get(), curInstruction[7].u.structureChain.get()); @@ -2151,7 +2186,7 @@ void CodeBlock::finalizeUnconditionally() for (unsigned i = 0; i < m_llintCallLinkInfos.size(); ++i) { if (m_llintCallLinkInfos[i].isLinked() && !Heap::isMarked(m_llintCallLinkInfos[i].callee.get())) { if (verboseUnlinking) - dataLog("Clearing LLInt call from %p.\n", this); + dataLogF("Clearing LLInt call from %p.\n", this); m_llintCallLinkInfos[i].unlink(); } if (!!m_llintCallLinkInfos[i].lastSeenCallee && !Heap::isMarked(m_llintCallLinkInfos[i].lastSeenCallee.get())) @@ -2164,12 +2199,33 @@ void CodeBlock::finalizeUnconditionally() // Check if we're not live. If we are, then jettison. if (!(shouldImmediatelyAssumeLivenessDuringScan() || m_dfgData->livenessHasBeenProved)) { if (verboseUnlinking) - dataLog("Code block %p has dead weak references, jettisoning during GC.\n", this); + dataLogF("Code block %p (executable %p) has dead weak references, jettisoning during GC.\n", this, ownerExecutable()); // Make sure that the baseline JIT knows that it should re-warm-up before // optimizing. alternative()->optimizeAfterWarmUp(); + if (DFG::shouldShowDisassembly()) { + dataLogF("DFG CodeBlock %p will be jettisoned because of the following dead references:\n", this); + for (unsigned i = 0; i < m_dfgData->transitions.size(); ++i) { + WeakReferenceTransition& transition = m_dfgData->transitions[i]; + JSCell* origin = transition.m_codeOrigin.get(); + JSCell* from = transition.m_from.get(); + JSCell* to = transition.m_to.get(); + if ((!origin || Heap::isMarked(origin)) && Heap::isMarked(from)) + continue; + dataLogF(" Transition under %s, ", JSValue(origin).description()); + dataLogF("%s -> ", JSValue(from).description()); + dataLogF("%s.\n", JSValue(to).description()); + } + for (unsigned i = 0; i < m_dfgData->weakReferences.size(); ++i) { + JSCell* weak = m_dfgData->weakReferences[i].get(); + if (Heap::isMarked(weak)) + continue; + dataLogF(" Weak reference %s.\n", JSValue(weak).description()); + } + } + jettison(); return; } @@ -2178,7 +2234,7 @@ void CodeBlock::finalizeUnconditionally() for (size_t size = m_putToBaseOperations.size(), i = 0; i < size; ++i) { if (m_putToBaseOperations[i].m_structure && !Heap::isMarked(m_putToBaseOperations[i].m_structure.get())) { if (verboseUnlinking) - dataLog("Clearing putToBase info in %p.\n", this); + dataLogF("Clearing putToBase info in %p.\n", this); m_putToBaseOperations[i].m_structure.clear(); } } @@ -2192,7 +2248,7 @@ void CodeBlock::finalizeUnconditionally() m_resolveOperations[i].last().m_structure.clear(); if (m_resolveOperations[i].last().m_structure && !Heap::isMarked(m_resolveOperations[i].last().m_structure.get())) { if (verboseUnlinking) - dataLog("Clearing resolve info in %p.\n", this); + dataLogF("Clearing resolve info in %p.\n", this); m_resolveOperations[i].last().m_structure.clear(); } } @@ -2202,10 +2258,19 @@ void CodeBlock::finalizeUnconditionally() if (!!getJITCode()) { RepatchBuffer repatchBuffer(this); for (unsigned i = 0; i < numberOfCallLinkInfos(); ++i) { - if (callLinkInfo(i).isLinked() && !Heap::isMarked(callLinkInfo(i).callee.get())) { - if (verboseUnlinking) - dataLog("Clearing call from %p to %p.\n", this, callLinkInfo(i).callee.get()); - callLinkInfo(i).unlink(*m_globalData, repatchBuffer); + if (callLinkInfo(i).isLinked()) { + if (ClosureCallStubRoutine* stub = callLinkInfo(i).stub.get()) { + if (!Heap::isMarked(stub->structure()) + || !Heap::isMarked(stub->executable())) { + if (verboseUnlinking) + dataLogF("Clearing closure call from %p to %p, stub routine %p.\n", this, stub->executable(), stub); + callLinkInfo(i).unlink(*m_globalData, repatchBuffer); + } + } else if (!Heap::isMarked(callLinkInfo(i).callee.get())) { + if (verboseUnlinking) + dataLogF("Clearing call from %p to %p.\n", this, callLinkInfo(i).callee.get()); + callLinkInfo(i).unlink(*m_globalData, repatchBuffer); + } } if (!!callLinkInfo(i).lastSeenCallee && !Heap::isMarked(callLinkInfo(i).lastSeenCallee.get())) @@ -2238,7 +2303,7 @@ void CodeBlock::resetStubInternal(RepatchBuffer& repatchBuffer, StructureStubInf AccessType accessType = static_cast<AccessType>(stubInfo.accessType); if (verboseUnlinking) - dataLog("Clearing structure cache (kind %d) in %p.\n", stubInfo.accessType, this); + dataLogF("Clearing structure cache (kind %d) in %p.\n", stubInfo.accessType, this); if (isGetByIdAccess(accessType)) { if (getJITCode().jitType() == JITCode::DFGJIT) @@ -2560,6 +2625,35 @@ Instruction* CodeBlock::adjustPCIfAtCallSite(Instruction* potentialReturnPC) } #endif // ENABLE(LLINT) +#if ENABLE(JIT) +ClosureCallStubRoutine* CodeBlock::findClosureCallForReturnPC(ReturnAddressPtr returnAddress) +{ + for (unsigned i = m_callLinkInfos.size(); i--;) { + CallLinkInfo& info = m_callLinkInfos[i]; + if (!info.stub) + continue; + if (!info.stub->code().executableMemory()->contains(returnAddress.value())) + continue; + + return info.stub.get(); + } + + // The stub routine may have been jettisoned. This is rare, but we have to handle it. + const JITStubRoutineSet& set = m_globalData->heap.jitStubRoutines(); + for (unsigned i = set.size(); i--;) { + GCAwareJITStubRoutine* genericStub = set.at(i); + if (!genericStub->isClosureCall()) + continue; + ClosureCallStubRoutine* stub = static_cast<ClosureCallStubRoutine*>(genericStub); + if (!stub->code().executableMemory()->contains(returnAddress.value())) + continue; + return stub; + } + + return 0; +} +#endif + unsigned CodeBlock::bytecodeOffset(ExecState* exec, ReturnAddressPtr returnAddress) { UNUSED_PARAM(exec); @@ -2597,7 +2691,16 @@ unsigned CodeBlock::bytecodeOffset(ExecState* exec, ReturnAddressPtr returnAddre Vector<CallReturnOffsetToBytecodeOffset>& callIndices = m_rareData->m_callReturnIndexVector; if (!callIndices.size()) return 1; - return binarySearch<CallReturnOffsetToBytecodeOffset, unsigned, getCallReturnOffset>(callIndices.begin(), callIndices.size(), getJITCode().offsetOf(returnAddress.value()))->bytecodeOffset; + + if (getJITCode().getExecutableMemory()->contains(returnAddress.value())) { + unsigned callReturnOffset = getJITCode().offsetOf(returnAddress.value()); + CallReturnOffsetToBytecodeOffset* result = + binarySearch<CallReturnOffsetToBytecodeOffset, unsigned, getCallReturnOffset>(callIndices.begin(), callIndices.size(), callReturnOffset); + ASSERT(result->callReturnOffset == callReturnOffset); + return result->bytecodeOffset; + } + + return findClosureCallForReturnPC(returnAddress)->codeOrigin().bytecodeIndex; #endif // ENABLE(JIT) #if !ENABLE(LLINT) && !ENABLE(JIT) @@ -2605,6 +2708,26 @@ unsigned CodeBlock::bytecodeOffset(ExecState* exec, ReturnAddressPtr returnAddre #endif } +#if ENABLE(DFG_JIT) +bool CodeBlock::codeOriginForReturn(ReturnAddressPtr returnAddress, CodeOrigin& codeOrigin) +{ + if (!hasCodeOrigins()) + return false; + + if (!getJITCode().getExecutableMemory()->contains(returnAddress.value())) { + codeOrigin = findClosureCallForReturnPC(returnAddress)->codeOrigin(); + return true; + } + + unsigned offset = getJITCode().offsetOf(returnAddress.value()); + CodeOriginAtCallReturnOffset* entry = binarySearch<CodeOriginAtCallReturnOffset, unsigned, getCallReturnOffsetForCodeOrigin>(codeOrigins().begin(), codeOrigins().size(), offset, WTF::KeyMustNotBePresentInArray); + if (entry->callReturnOffset != offset) + return false; + codeOrigin = entry->codeOrigin; + return true; +} +#endif // ENABLE(DFG_JIT) + void CodeBlock::clearEvalCache() { if (!!m_alternative) @@ -2645,6 +2768,8 @@ void CodeBlock::reoptimize() ASSERT(replacement() != this); ASSERT(replacement()->alternative() == this); replacement()->tallyFrequentExitSites(); + if (DFG::shouldShowDisassembly()) + dataLogF("DFG CodeBlock %p will be jettisoned due to reoptimization of %p.\n", replacement(), this); replacement()->jettison(); countReoptimization(); optimizeAfterWarmUp(); @@ -2710,6 +2835,8 @@ void ProgramCodeBlock::jettison() { ASSERT(JITCode::isOptimizingJIT(getJITType())); ASSERT(this == replacement()); + if (DFG::shouldShowDisassembly()) + dataLogF("Jettisoning DFG CodeBlock %p.\n", this); static_cast<ProgramExecutable*>(ownerExecutable())->jettisonOptimizedCode(*globalData()); } @@ -2717,6 +2844,8 @@ void EvalCodeBlock::jettison() { ASSERT(JITCode::isOptimizingJIT(getJITType())); ASSERT(this == replacement()); + if (DFG::shouldShowDisassembly()) + dataLogF("Jettisoning DFG CodeBlock %p.\n", this); static_cast<EvalExecutable*>(ownerExecutable())->jettisonOptimizedCode(*globalData()); } @@ -2724,6 +2853,8 @@ void FunctionCodeBlock::jettison() { ASSERT(JITCode::isOptimizingJIT(getJITType())); ASSERT(this == replacement()); + if (DFG::shouldShowDisassembly()) + dataLogF("Jettisoning DFG CodeBlock %p.\n", this); static_cast<FunctionExecutable*>(ownerExecutable())->jettisonOptimizedCodeFor(*globalData(), m_isConstructor ? CodeForConstruct : CodeForCall); } @@ -2790,24 +2921,34 @@ void CodeBlock::updateAllPredictionsAndCountLiveness( #if ENABLE(DFG_JIT) m_lazyOperandValueProfiles.computeUpdatedPredictions(operation); #endif - - // Don't count the array profiles towards statistics, since each array profile - // site also has a value profile site - so we already know whether or not it's - // live. +} + +void CodeBlock::updateAllValueProfilePredictions(OperationInProgress operation) +{ + unsigned ignoredValue1, ignoredValue2; + updateAllPredictionsAndCountLiveness(operation, ignoredValue1, ignoredValue2); +} + +void CodeBlock::updateAllArrayPredictions(OperationInProgress operation) +{ for (unsigned i = m_arrayProfiles.size(); i--;) m_arrayProfiles[i].computeUpdatedPrediction(this, operation); + + // Don't count these either, for similar reasons. + for (unsigned i = m_arrayAllocationProfiles.size(); i--;) + m_arrayAllocationProfiles[i].updateIndexingType(); } void CodeBlock::updateAllPredictions(OperationInProgress operation) { - unsigned ignoredValue1, ignoredValue2; - updateAllPredictionsAndCountLiveness(operation, ignoredValue1, ignoredValue2); + updateAllValueProfilePredictions(operation); + updateAllArrayPredictions(operation); } bool CodeBlock::shouldOptimizeNow() { #if ENABLE(JIT_VERBOSE_OSR) - dataLog("Considering optimizing %p...\n", this); + dataLogF("Considering optimizing %p...\n", this); #endif #if ENABLE(VERBOSE_VALUE_PROFILE) @@ -2817,12 +2958,14 @@ bool CodeBlock::shouldOptimizeNow() if (m_optimizationDelayCounter >= Options::maximumOptimizationDelay()) return true; + updateAllArrayPredictions(); + unsigned numberOfLiveNonArgumentValueProfiles; unsigned numberOfSamplesInProfiles; updateAllPredictionsAndCountLiveness(NoOperation, numberOfLiveNonArgumentValueProfiles, numberOfSamplesInProfiles); #if ENABLE(JIT_VERBOSE_OSR) - dataLog("Profile hotness: %lf, %lf\n", (double)numberOfLiveNonArgumentValueProfiles / numberOfValueProfiles(), (double)numberOfSamplesInProfiles / ValueProfile::numberOfBuckets / numberOfValueProfiles()); + dataLogF("Profile hotness: %lf (%u / %u), %lf (%u / %u)\n", (double)numberOfLiveNonArgumentValueProfiles / numberOfValueProfiles(), numberOfLiveNonArgumentValueProfiles, numberOfValueProfiles(), (double)numberOfSamplesInProfiles / ValueProfile::numberOfBuckets / numberOfValueProfiles(), numberOfSamplesInProfiles, ValueProfile::numberOfBuckets * numberOfValueProfiles()); #endif if ((!numberOfValueProfiles() || (double)numberOfLiveNonArgumentValueProfiles / numberOfValueProfiles() >= Options::desiredProfileLivenessRate()) @@ -2853,7 +2996,7 @@ void CodeBlock::tallyFrequentExitSites() continue; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("OSR exit #%u (bc#%u, @%u, %s) for code block %p occurred frequently; counting as frequent exit site.\n", i, exit.m_codeOrigin.bytecodeIndex, exit.m_nodeIndex, DFG::exitKindToString(exit.m_kind), this); + dataLogF("OSR exit #%u (bc#%u, @%u, %s) for code block %p occurred frequently; counting as frequent exit site.\n", i, exit.m_codeOrigin.bytecodeIndex, exit.m_nodeIndex, DFG::exitKindToString(exit.m_kind), this); #endif } } @@ -2862,30 +3005,30 @@ void CodeBlock::tallyFrequentExitSites() #if ENABLE(VERBOSE_VALUE_PROFILE) void CodeBlock::dumpValueProfiles() { - dataLog("ValueProfile for %p:\n", this); + dataLogF("ValueProfile for %p:\n", this); for (unsigned i = 0; i < totalNumberOfValueProfiles(); ++i) { ValueProfile* profile = getFromAllValueProfiles(i); if (profile->m_bytecodeOffset < 0) { ASSERT(profile->m_bytecodeOffset == -1); - dataLog(" arg = %u: ", i); + dataLogF(" arg = %u: ", i); } else - dataLog(" bc = %d: ", profile->m_bytecodeOffset); + dataLogF(" bc = %d: ", profile->m_bytecodeOffset); if (!profile->numberOfSamples() && profile->m_prediction == SpecNone) { - dataLog("<empty>\n"); + dataLogF("<empty>\n"); continue; } profile->dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); } - dataLog("RareCaseProfile for %p:\n", this); + dataLogF("RareCaseProfile for %p:\n", this); for (unsigned i = 0; i < numberOfRareCaseProfiles(); ++i) { RareCaseProfile* profile = rareCaseProfile(i); - dataLog(" bc = %d: %u\n", profile->m_bytecodeOffset, profile->m_counter); + dataLogF(" bc = %d: %u\n", profile->m_bytecodeOffset, profile->m_counter); } - dataLog("SpecialFastCaseProfile for %p:\n", this); + dataLogF("SpecialFastCaseProfile for %p:\n", this); for (unsigned i = 0; i < numberOfSpecialFastCaseProfiles(); ++i) { RareCaseProfile* profile = specialFastCaseProfile(i); - dataLog(" bc = %d: %u\n", profile->m_bytecodeOffset, profile->m_counter); + dataLogF(" bc = %d: %u\n", profile->m_bytecodeOffset, profile->m_counter); } } #endif // ENABLE(VERBOSE_VALUE_PROFILE) diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.h b/Source/JavaScriptCore/bytecode/CodeBlock.h index a28064940..63a03630e 100644 --- a/Source/JavaScriptCore/bytecode/CodeBlock.h +++ b/Source/JavaScriptCore/bytecode/CodeBlock.h @@ -163,7 +163,8 @@ namespace JSC { static void dumpStatistics(); - void dump(ExecState*); + void dump(); + void dump(unsigned bytecodeOffset); void printStructures(const Instruction*); void printStructure(const char* name, const Instruction*, int operand); @@ -245,6 +246,7 @@ namespace JSC { CallLinkInfo& getCallLinkInfo(unsigned bytecodeIndex) { + ASSERT(JITCode::isBaselineCode(getJITType())); return *(binarySearch<CallLinkInfo, unsigned, getCallLinkInfoBytecodeIndex>(m_callLinkInfos.begin(), m_callLinkInfos.size(), bytecodeIndex)); } #endif // ENABLE(JIT) @@ -274,6 +276,11 @@ namespace JSC { { m_incomingCalls.push(incoming); } + + bool isIncomingCallAlreadyLinked(CallLinkInfo* incoming) + { + return m_incomingCalls.isOnList(incoming); + } #endif // ENABLE(JIT) #if ENABLE(LLINT) @@ -755,6 +762,13 @@ namespace JSC { } ArrayProfile* getArrayProfile(unsigned bytecodeOffset); ArrayProfile* getOrAddArrayProfile(unsigned bytecodeOffset); + + unsigned numberOfArrayAllocationProfiles() const { return m_arrayAllocationProfiles.size(); } + ArrayAllocationProfile* addArrayAllocationProfile() + { + m_arrayAllocationProfiles.append(ArrayAllocationProfile()); + return &m_arrayAllocationProfiles.last(); + } #endif // Exception handling support @@ -806,17 +820,7 @@ namespace JSC { return m_rareData && !!m_rareData->m_codeOrigins.size(); } - bool codeOriginForReturn(ReturnAddressPtr returnAddress, CodeOrigin& codeOrigin) - { - if (!hasCodeOrigins()) - return false; - unsigned offset = getJITCode().offsetOf(returnAddress.value()); - CodeOriginAtCallReturnOffset* entry = binarySearch<CodeOriginAtCallReturnOffset, unsigned, getCallReturnOffsetForCodeOrigin>(codeOrigins().begin(), codeOrigins().size(), offset, WTF::KeyMustNotBePresentInArray); - if (entry->callReturnOffset != offset) - return false; - codeOrigin = entry->codeOrigin; - return true; - } + bool codeOriginForReturn(ReturnAddressPtr, CodeOrigin&); CodeOrigin codeOrigin(unsigned index) { @@ -1145,9 +1149,13 @@ namespace JSC { #if ENABLE(VALUE_PROFILER) bool shouldOptimizeNow(); + void updateAllValueProfilePredictions(OperationInProgress = NoOperation); + void updateAllArrayPredictions(OperationInProgress = NoOperation); void updateAllPredictions(OperationInProgress = NoOperation); #else bool shouldOptimizeNow() { return false; } + void updateAllValueProfilePredictions(OperationInProgress = NoOperation) { } + void updateAllArrayPredictions(OperationInProgress = NoOperation) { } void updateAllPredictions(OperationInProgress = NoOperation) { } #endif @@ -1176,6 +1184,10 @@ namespace JSC { private: friend class DFGCodeBlocks; + +#if ENABLE(JIT) + ClosureCallStubRoutine* findClosureCallForReturnPC(ReturnAddressPtr); +#endif #if ENABLE(DFG_JIT) void tallyFrequentExitSites(); @@ -1200,17 +1212,17 @@ namespace JSC { m_constantRegisters[i].set(*m_globalData, ownerExecutable(), constants[i].get()); } - void dump(ExecState*, const Vector<Instruction>::const_iterator& begin, Vector<Instruction>::const_iterator&); + void dump(ExecState*, const Instruction* begin, const Instruction*&); CString registerName(ExecState*, int r) const; - void printUnaryOp(ExecState*, int location, Vector<Instruction>::const_iterator&, const char* op); - void printBinaryOp(ExecState*, int location, Vector<Instruction>::const_iterator&, const char* op); - void printConditionalJump(ExecState*, const Vector<Instruction>::const_iterator&, Vector<Instruction>::const_iterator&, int location, const char* op); - void printGetByIdOp(ExecState*, int location, Vector<Instruction>::const_iterator&); + void printUnaryOp(ExecState*, int location, const Instruction*&, const char* op); + void printBinaryOp(ExecState*, int location, const Instruction*&, const char* op); + void printConditionalJump(ExecState*, const Instruction*, const Instruction*&, int location, const char* op); + void printGetByIdOp(ExecState*, int location, const Instruction*&); void printGetByIdCacheStatus(ExecState*, int location); enum CacheDumpMode { DumpCaches, DontDumpCaches }; - void printCallOp(ExecState*, int location, Vector<Instruction>::const_iterator&, const char* op, CacheDumpMode); - void printPutByIdOp(ExecState*, int location, Vector<Instruction>::const_iterator&, const char* op); + void printCallOp(ExecState*, int location, const Instruction*&, const char* op, CacheDumpMode); + void printPutByIdOp(ExecState*, int location, const Instruction*&, const char* op); void visitStructures(SlotVisitor&, Instruction* vPC); #if ENABLE(DFG_JIT) @@ -1228,6 +1240,9 @@ namespace JSC { // allow them to continue to execute soundly. if (m_dfgData->mayBeExecuting) return true; + + if (Options::forceDFGCodeBlockLiveness()) + return true; return false; } @@ -1330,6 +1345,7 @@ namespace JSC { SegmentedVector<ValueProfile, 8> m_valueProfiles; SegmentedVector<RareCaseProfile, 8> m_rareCaseProfiles; SegmentedVector<RareCaseProfile, 8> m_specialFastCaseProfiles; + SegmentedVector<ArrayAllocationProfile, 8> m_arrayAllocationProfiles; ArrayProfileVector m_arrayProfiles; unsigned m_executionEntryCount; #endif diff --git a/Source/JavaScriptCore/bytecode/DFGExitProfile.h b/Source/JavaScriptCore/bytecode/DFGExitProfile.h index 60d313ad4..7132adfd4 100644 --- a/Source/JavaScriptCore/bytecode/DFGExitProfile.h +++ b/Source/JavaScriptCore/bytecode/DFGExitProfile.h @@ -58,6 +58,8 @@ inline const char* exitKindToString(ExitKind kind) return "BadCache"; case BadWeakConstantCache: return "BadWeakConstantCache"; + case BadIndexingType: + return "BadIndexingType"; case Overflow: return "Overflow"; case NegativeZero: diff --git a/Source/JavaScriptCore/bytecode/GetByIdStatus.cpp b/Source/JavaScriptCore/bytecode/GetByIdStatus.cpp index 605a81c2f..d17c17325 100644 --- a/Source/JavaScriptCore/bytecode/GetByIdStatus.cpp +++ b/Source/JavaScriptCore/bytecode/GetByIdStatus.cpp @@ -151,7 +151,7 @@ GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, unsigned bytec // Finally figure out if we can derive an access strategy. GetByIdStatus result; - result.m_wasSeenInJIT = true; + result.m_wasSeenInJIT = true; // This is interesting for bytecode dumping only. switch (stubInfo.accessType) { case access_unset: return computeFromLLInt(profiledBlock, bytecodeIndex, ident); @@ -252,5 +252,35 @@ GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, unsigned bytec #endif // ENABLE(JIT) } +GetByIdStatus GetByIdStatus::computeFor(JSGlobalData& globalData, Structure* structure, Identifier& ident) +{ + // For now we only handle the super simple self access case. We could handle the + // prototype case in the future. + + if (PropertyName(ident).asIndex() != PropertyName::NotAnIndex) + return GetByIdStatus(TakesSlowPath); + + if (structure->typeInfo().overridesGetOwnPropertySlot()) + return GetByIdStatus(TakesSlowPath); + + if (!structure->propertyAccessesAreCacheable()) + return GetByIdStatus(TakesSlowPath); + + GetByIdStatus result; + result.m_wasSeenInJIT = false; // To my knowledge nobody that uses computeFor(JSGlobalData&, Structure*, Identifier&) reads this field, but I might as well be honest: no, it wasn't seen in the JIT, since I computed it statically. + unsigned attributes; + JSCell* specificValue; + result.m_offset = structure->get(globalData, ident, attributes, specificValue); + if (!isValidOffset(result.m_offset)) + return GetByIdStatus(TakesSlowPath); // It's probably a prototype lookup. Give up on life for now, even though we could totally be way smarter about it. + if (attributes & Accessor) + return GetByIdStatus(MakesCalls); + if (structure->isDictionary()) + specificValue = 0; + result.m_structureSet.add(structure); + result.m_specificValue = JSValue(specificValue); + return result; +} + } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/GetByIdStatus.h b/Source/JavaScriptCore/bytecode/GetByIdStatus.h index f38a19e8c..45d8c0b1f 100644 --- a/Source/JavaScriptCore/bytecode/GetByIdStatus.h +++ b/Source/JavaScriptCore/bytecode/GetByIdStatus.h @@ -51,6 +51,13 @@ public: { } + explicit GetByIdStatus(State state) + : m_state(state) + , m_offset(invalidOffset) + { + ASSERT(state == NoInformation || state == TakesSlowPath || state == MakesCalls); + } + GetByIdStatus( State state, bool wasSeenInJIT, const StructureSet& structureSet = StructureSet(), PropertyOffset offset = invalidOffset, JSValue specificValue = JSValue(), Vector<Structure*> chain = Vector<Structure*>()) @@ -65,6 +72,7 @@ public: } static GetByIdStatus computeFor(CodeBlock*, unsigned bytecodeIndex, Identifier&); + static GetByIdStatus computeFor(JSGlobalData&, Structure*, Identifier&); State state() const { return m_state; } diff --git a/Source/JavaScriptCore/bytecode/Instruction.h b/Source/JavaScriptCore/bytecode/Instruction.h index 9fcf509f6..50b80e03c 100644 --- a/Source/JavaScriptCore/bytecode/Instruction.h +++ b/Source/JavaScriptCore/bytecode/Instruction.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -47,6 +47,7 @@ namespace JSC { // curently actually use PolymorphicAccessStructureLists, which we should). Anyway, this seems like the best // solution for now - will need to something smarter if/when we actually want mixed-mode operation. + class ArrayAllocationProfile; class ArrayProfile; class JSCell; class Structure; @@ -193,6 +194,7 @@ namespace JSC { Instruction(ValueProfile* profile) { u.profile = profile; } Instruction(ArrayProfile* profile) { u.arrayProfile = profile; } + Instruction(ArrayAllocationProfile* profile) { u.arrayAllocationProfile = profile; } Instruction(WriteBarrier<Unknown>* registerPointer) { u.registerPointer = registerPointer; } @@ -212,6 +214,7 @@ namespace JSC { LLIntCallLinkInfo* callLinkInfo; ValueProfile* profile; ArrayProfile* arrayProfile; + ArrayAllocationProfile* arrayAllocationProfile; void* pointer; bool* predicatePointer; } u; diff --git a/Source/JavaScriptCore/bytecode/Opcode.cpp b/Source/JavaScriptCore/bytecode/Opcode.cpp index a27714026..0adc76b28 100644 --- a/Source/JavaScriptCore/bytecode/Opcode.cpp +++ b/Source/JavaScriptCore/bytecode/Opcode.cpp @@ -114,19 +114,19 @@ OpcodeStats::~OpcodeStats() *(currentPairIndex++) = make_pair(i, j); qsort(sortedPairIndices, numOpcodeIDs * numOpcodeIDs, sizeof(pair<int, int>), compareOpcodePairIndices); - dataLog("\nExecuted opcode statistics\n"); + dataLogF("\nExecuted opcode statistics\n"); - dataLog("Total instructions executed: %lld\n\n", totalInstructions); + dataLogF("Total instructions executed: %lld\n\n", totalInstructions); - dataLog("All opcodes by frequency:\n\n"); + dataLogF("All opcodes by frequency:\n\n"); for (int i = 0; i < numOpcodeIDs; ++i) { int index = sortedIndices[i]; - dataLog("%s:%s %lld - %.2f%%\n", opcodeNames[index], padOpcodeName((OpcodeID)index, 28), opcodeCounts[index], ((double) opcodeCounts[index]) / ((double) totalInstructions) * 100.0); + dataLogF("%s:%s %lld - %.2f%%\n", opcodeNames[index], padOpcodeName((OpcodeID)index, 28), opcodeCounts[index], ((double) opcodeCounts[index]) / ((double) totalInstructions) * 100.0); } - dataLog("\n"); - dataLog("2-opcode sequences by frequency: %lld\n\n", totalInstructions); + dataLogF("\n"); + dataLogF("2-opcode sequences by frequency: %lld\n\n", totalInstructions); for (int i = 0; i < numOpcodeIDs * numOpcodeIDs; ++i) { pair<int, int> indexPair = sortedPairIndices[i]; @@ -135,11 +135,11 @@ OpcodeStats::~OpcodeStats() if (!count) break; - dataLog("%s%s %s:%s %lld %.2f%%\n", opcodeNames[indexPair.first], padOpcodeName((OpcodeID)indexPair.first, 28), opcodeNames[indexPair.second], padOpcodeName((OpcodeID)indexPair.second, 28), count, ((double) count) / ((double) totalInstructionPairs) * 100.0); + dataLogF("%s%s %s:%s %lld %.2f%%\n", opcodeNames[indexPair.first], padOpcodeName((OpcodeID)indexPair.first, 28), opcodeNames[indexPair.second], padOpcodeName((OpcodeID)indexPair.second, 28), count, ((double) count) / ((double) totalInstructionPairs) * 100.0); } - dataLog("\n"); - dataLog("Most common opcodes and sequences:\n"); + dataLogF("\n"); + dataLogF("Most common opcodes and sequences:\n"); for (int i = 0; i < numOpcodeIDs; ++i) { int index = sortedIndices[i]; @@ -147,7 +147,7 @@ OpcodeStats::~OpcodeStats() double opcodeProportion = ((double) opcodeCount) / ((double) totalInstructions); if (opcodeProportion < 0.0001) break; - dataLog("\n%s:%s %lld - %.2f%%\n", opcodeNames[index], padOpcodeName((OpcodeID)index, 28), opcodeCount, opcodeProportion * 100.0); + dataLogF("\n%s:%s %lld - %.2f%%\n", opcodeNames[index], padOpcodeName((OpcodeID)index, 28), opcodeCount, opcodeProportion * 100.0); for (int j = 0; j < numOpcodeIDs * numOpcodeIDs; ++j) { pair<int, int> indexPair = sortedPairIndices[j]; @@ -160,11 +160,11 @@ OpcodeStats::~OpcodeStats() if (indexPair.first != index && indexPair.second != index) continue; - dataLog(" %s%s %s:%s %lld - %.2f%%\n", opcodeNames[indexPair.first], padOpcodeName((OpcodeID)indexPair.first, 28), opcodeNames[indexPair.second], padOpcodeName((OpcodeID)indexPair.second, 28), pairCount, pairProportion * 100.0); + dataLogF(" %s%s %s:%s %lld - %.2f%%\n", opcodeNames[indexPair.first], padOpcodeName((OpcodeID)indexPair.first, 28), opcodeNames[indexPair.second], padOpcodeName((OpcodeID)indexPair.second, 28), pairCount, pairProportion * 100.0); } } - dataLog("\n"); + dataLogF("\n"); } void OpcodeStats::recordInstruction(int opcode) diff --git a/Source/JavaScriptCore/bytecode/Opcode.h b/Source/JavaScriptCore/bytecode/Opcode.h index 8979d0b7b..5fe28bc09 100644 --- a/Source/JavaScriptCore/bytecode/Opcode.h +++ b/Source/JavaScriptCore/bytecode/Opcode.h @@ -44,13 +44,14 @@ namespace JSC { macro(op_create_activation, 2) \ macro(op_init_lazy_reg, 2) \ macro(op_create_arguments, 2) \ - macro(op_create_this, 2) \ + macro(op_create_this, 3) \ + macro(op_get_callee, 3) \ macro(op_convert_this, 3) \ \ macro(op_new_object, 2) \ - macro(op_new_array, 4) \ - macro(op_new_array_with_size, 3) \ - macro(op_new_array_buffer, 4) \ + macro(op_new_array, 5) \ + macro(op_new_array_with_size, 4) \ + macro(op_new_array_buffer, 5) \ macro(op_new_regexp, 3) \ macro(op_mov, 3) \ \ diff --git a/Source/JavaScriptCore/bytecode/Operands.h b/Source/JavaScriptCore/bytecode/Operands.h index b0f0e692c..0cea096cf 100644 --- a/Source/JavaScriptCore/bytecode/Operands.h +++ b/Source/JavaScriptCore/bytecode/Operands.h @@ -190,7 +190,7 @@ private: }; template<typename T, typename Traits> -void dumpOperands(Operands<T, Traits>& operands, FILE* out) +void dumpOperands(const Operands<T, Traits>& operands, FILE* out) { for (size_t argument = 0; argument < operands.numberOfArguments(); ++argument) { if (argument) @@ -205,16 +205,6 @@ void dumpOperands(Operands<T, Traits>& operands, FILE* out) } } -template<typename T, typename Traits> -void dumpOperands(const Operands<T, Traits>& operands, FILE* out) -{ - // Use const-cast because: - // 1) I don't feel like writing this code twice, and - // 2) Some dump() methods may not be const, and I don't really care if that's - // the case. - dumpOperands(*const_cast<Operands<T, Traits>*>(&operands), out); -} - } // namespace JSC #endif // Operands_h diff --git a/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp b/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp index 35800f3dd..7d6ba0987 100644 --- a/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp +++ b/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp @@ -29,6 +29,7 @@ #include "CodeBlock.h" #include "LLIntData.h" #include "LowLevelInterpreter.h" +#include "Operations.h" #include "Structure.h" #include "StructureChain.h" @@ -134,5 +135,77 @@ PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, unsigned bytec #endif // ENABLE(JIT) } +PutByIdStatus PutByIdStatus::computeFor(JSGlobalData& globalData, JSGlobalObject* globalObject, Structure* structure, Identifier& ident, bool isDirect) +{ + if (PropertyName(ident).asIndex() != PropertyName::NotAnIndex) + return PutByIdStatus(TakesSlowPath); + + if (structure->typeInfo().overridesGetOwnPropertySlot()) + return PutByIdStatus(TakesSlowPath); + + if (!structure->propertyAccessesAreCacheable()) + return PutByIdStatus(TakesSlowPath); + + unsigned attributes; + JSCell* specificValueIgnored; + PropertyOffset offset = structure->get(globalData, ident, attributes, specificValueIgnored); + if (isValidOffset(offset)) { + if (attributes & (Accessor | ReadOnly)) + return PutByIdStatus(TakesSlowPath); + return PutByIdStatus(SimpleReplace, structure, 0, 0, offset); + } + + // Our hypothesis is that we're doing a transition. Before we prove that this is really + // true, we want to do some sanity checks. + + // Don't cache put transitions on dictionaries. + if (structure->isDictionary()) + return PutByIdStatus(TakesSlowPath); + + // If the structure corresponds to something that isn't an object, then give up, since + // we don't want to be adding properties to strings. + if (structure->typeInfo().type() == StringType) + return PutByIdStatus(TakesSlowPath); + + if (!isDirect) { + // If the prototype chain has setters or read-only properties, then give up. + if (structure->prototypeChainMayInterceptStoreTo(globalData, ident)) + return PutByIdStatus(TakesSlowPath); + + // If the prototype chain hasn't been normalized (i.e. there are proxies or dictionaries) + // then give up. The dictionary case would only happen if this structure has not been + // used in an optimized put_by_id transition. And really the only reason why we would + // bail here is that I don't really feel like having the optimizing JIT go and flatten + // dictionaries if we have evidence to suggest that those objects were never used as + // prototypes in a cacheable prototype access - i.e. there's a good chance that some of + // the other checks below will fail. + if (!isPrototypeChainNormalized(globalObject, structure)) + return PutByIdStatus(TakesSlowPath); + } + + // We only optimize if there is already a structure that the transition is cached to. + // Among other things, this allows us to guard against a transition with a specific + // value. + // + // - If we're storing a value that could be specific: this would only be a problem if + // the existing transition did have a specific value already, since if it didn't, + // then we would behave "as if" we were not storing a specific value. If it did + // have a specific value, then we'll know - the fact that we pass 0 for + // specificValue will tell us. + // + // - If we're not storing a value that could be specific: again, this would only be a + // problem if the existing transition did have a specific value, which we check for + // by passing 0 for the specificValue. + Structure* transition = Structure::addPropertyTransitionToExistingStructure(structure, ident, 0, 0, offset); + if (!transition) + return PutByIdStatus(TakesSlowPath); // This occurs in bizarre cases only. See above. + ASSERT(!transition->transitionDidInvolveSpecificValue()); + ASSERT(isValidOffset(offset)); + + return PutByIdStatus( + SimpleTransition, structure, transition, + structure->prototypeChain(globalData, globalObject), offset); +} + } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/PutByIdStatus.h b/Source/JavaScriptCore/bytecode/PutByIdStatus.h index 694915244..fe22009fe 100644 --- a/Source/JavaScriptCore/bytecode/PutByIdStatus.h +++ b/Source/JavaScriptCore/bytecode/PutByIdStatus.h @@ -33,6 +33,8 @@ namespace JSC { class CodeBlock; class Identifier; +class JSGlobalData; +class JSGlobalObject; class Structure; class StructureChain; @@ -60,6 +62,16 @@ public: { } + explicit PutByIdStatus(State state) + : m_state(state) + , m_oldStructure(0) + , m_newStructure(0) + , m_structureChain(0) + , m_offset(invalidOffset) + { + ASSERT(m_state == NoInformation || m_state == TakesSlowPath); + } + PutByIdStatus( State state, Structure* oldStructure, @@ -79,6 +91,7 @@ public: } static PutByIdStatus computeFor(CodeBlock*, unsigned bytecodeIndex, Identifier&); + static PutByIdStatus computeFor(JSGlobalData&, JSGlobalObject*, Structure*, Identifier&, bool isDirect); State state() const { return m_state; } diff --git a/Source/JavaScriptCore/bytecode/SamplingTool.cpp b/Source/JavaScriptCore/bytecode/SamplingTool.cpp index f9b8245e5..a76fee179 100644 --- a/Source/JavaScriptCore/bytecode/SamplingTool.cpp +++ b/Source/JavaScriptCore/bytecode/SamplingTool.cpp @@ -67,14 +67,14 @@ void SamplingFlags::stop() total += s_flagCounts[i]; if (total) { - dataLog("\nSamplingFlags: sample counts with flags set: (%lld total)\n", total); + dataLogF("\nSamplingFlags: sample counts with flags set: (%lld total)\n", total); for (unsigned i = 0; i <= 32; ++i) { if (s_flagCounts[i]) - dataLog(" [ %02d ] : %lld\t\t(%03.2f%%)\n", i, s_flagCounts[i], (100.0 * s_flagCounts[i]) / total); + dataLogF(" [ %02d ] : %lld\t\t(%03.2f%%)\n", i, s_flagCounts[i], (100.0 * s_flagCounts[i]) / total); } - dataLog("\n"); + dataLogF("\n"); } else - dataLog("\nSamplingFlags: no samples.\n\n"); + dataLogF("\nSamplingFlags: no samples.\n\n"); } uint64_t SamplingFlags::s_flagCounts[33]; @@ -151,7 +151,7 @@ void SamplingRegion::dump() void SamplingRegion::dumpInternal() { if (!s_spectrum) { - dataLog("\nSamplingRegion: was never sampled.\n\n"); + dataLogF("\nSamplingRegion: was never sampled.\n\n"); return; } @@ -161,10 +161,10 @@ void SamplingRegion::dumpInternal() for (unsigned i = list.size(); i--;) total += list[i].count; - dataLog("\nSamplingRegion: sample counts for regions: (%lu samples)\n", total); + dataLogF("\nSamplingRegion: sample counts for regions: (%lu samples)\n", total); for (unsigned i = list.size(); i--;) - dataLog(" %3.2lf%% %s\n", (100.0 * list[i].count) / total, list[i].key); + dataLogF(" %3.2lf%% %s\n", (100.0 * list[i].count) / total, list[i].key); } #else // ENABLE(SAMPLING_REGIONS) void SamplingRegion::dump() { } @@ -371,10 +371,10 @@ void SamplingTool::dump(ExecState* exec) // (2) Print Opcode sampling results. - dataLog("\nBytecode samples [*]\n"); - dataLog(" sample %% of %% of | cti cti %%\n"); - dataLog("opcode count VM total | count of self\n"); - dataLog("------------------------------------------------------- | ----------------\n"); + dataLogF("\nBytecode samples [*]\n"); + dataLogF(" sample %% of %% of | cti cti %%\n"); + dataLogF("opcode count VM total | count of self\n"); + dataLogF("------------------------------------------------------- | ----------------\n"); for (int i = 0; i < numOpcodeIDs; ++i) { long long count = opcodeSampleInfo[i].count; @@ -392,15 +392,15 @@ void SamplingTool::dump(ExecState* exec) debugDebugPrintf("%s:%s%-6lld %.3f%%\t%.3f%%\t | %-6lld %.3f%%\n", opcodeName, opcodePadding, count, percentOfVM, percentOfTotal, countInCTIFunctions, percentInCTIFunctions); } - dataLog("\n[*] Samples inside host code are not charged to any Bytecode.\n\n"); - dataLog("\tSamples inside VM:\t\t%lld / %lld (%.3f%%)\n", m_opcodeSampleCount, m_sampleCount, (static_cast<double>(m_opcodeSampleCount) * 100) / m_sampleCount); - dataLog("\tSamples inside host code:\t%lld / %lld (%.3f%%)\n\n", m_sampleCount - m_opcodeSampleCount, m_sampleCount, (static_cast<double>(m_sampleCount - m_opcodeSampleCount) * 100) / m_sampleCount); - dataLog("\tsample count:\tsamples inside this opcode\n"); - dataLog("\t%% of VM:\tsample count / all opcode samples\n"); - dataLog("\t%% of total:\tsample count / all samples\n"); - dataLog("\t--------------\n"); - dataLog("\tcti count:\tsamples inside a CTI function called by this opcode\n"); - dataLog("\tcti %% of self:\tcti count / sample count\n"); + dataLogF("\n[*] Samples inside host code are not charged to any Bytecode.\n\n"); + dataLogF("\tSamples inside VM:\t\t%lld / %lld (%.3f%%)\n", m_opcodeSampleCount, m_sampleCount, (static_cast<double>(m_opcodeSampleCount) * 100) / m_sampleCount); + dataLogF("\tSamples inside host code:\t%lld / %lld (%.3f%%)\n\n", m_sampleCount - m_opcodeSampleCount, m_sampleCount, (static_cast<double>(m_sampleCount - m_opcodeSampleCount) * 100) / m_sampleCount); + dataLogF("\tsample count:\tsamples inside this opcode\n"); + dataLogF("\t%% of VM:\tsample count / all opcode samples\n"); + dataLogF("\t%% of total:\tsample count / all samples\n"); + dataLogF("\t--------------\n"); + dataLogF("\tcti count:\tsamples inside a CTI function called by this opcode\n"); + dataLogF("\tcti %% of self:\tcti count / sample count\n"); #if ENABLE(CODEBLOCK_SAMPLING) @@ -416,7 +416,7 @@ void SamplingTool::dump(ExecState* exec) // (4) Print data from 'codeBlockSamples' array. - dataLog("\nCodeBlock samples\n\n"); + dataLogF("\nCodeBlock samples\n\n"); for (int i = 0; i < scopeCount; ++i) { ScriptSampleRecord* record = codeBlockSamples[i]; @@ -426,21 +426,21 @@ void SamplingTool::dump(ExecState* exec) if (blockPercent >= 1) { //Instruction* code = codeBlock->instructions().begin(); - dataLog("#%d: %s:%d: %d / %lld (%.3f%%)\n", i + 1, record->m_executable->sourceURL().utf8().data(), codeBlock->lineNumberForBytecodeOffset(0), record->m_sampleCount, m_sampleCount, blockPercent); + dataLogF("#%d: %s:%d: %d / %lld (%.3f%%)\n", i + 1, record->m_executable->sourceURL().utf8().data(), codeBlock->lineNumberForBytecodeOffset(0), record->m_sampleCount, m_sampleCount, blockPercent); if (i < 10) { HashMap<unsigned,unsigned> lineCounts; codeBlock->dump(exec); - dataLog(" Opcode and line number samples [*]\n\n"); + dataLogF(" Opcode and line number samples [*]\n\n"); for (unsigned op = 0; op < record->m_size; ++op) { int count = record->m_samples[op]; if (count) { - dataLog(" [% 4d] has sample count: % 4d\n", op, count); + dataLogF(" [% 4d] has sample count: % 4d\n", op, count); unsigned line = codeBlock->lineNumberForBytecodeOffset(op); lineCounts.set(line, (lineCounts.contains(line) ? lineCounts.get(line) : 0) + count); } } - dataLog("\n"); + dataLogF("\n"); int linesCount = lineCounts.size(); Vector<LineCountInfo> lineCountInfo(linesCount); @@ -453,12 +453,12 @@ void SamplingTool::dump(ExecState* exec) qsort(lineCountInfo.begin(), linesCount, sizeof(LineCountInfo), compareLineCountInfoSampling); for (lineno = 0; lineno < linesCount; ++lineno) { - dataLog(" Line #%d has sample count %d.\n", lineCountInfo[lineno].line, lineCountInfo[lineno].count); + dataLogF(" Line #%d has sample count %d.\n", lineCountInfo[lineno].line, lineCountInfo[lineno].count); } - dataLog("\n"); - dataLog(" [*] Samples inside host code are charged to the calling Bytecode.\n"); - dataLog(" Samples on a call / return boundary are not charged to a specific opcode or line.\n\n"); - dataLog(" Samples on a call / return boundary: %d / %d (%.3f%%)\n\n", record->m_sampleCount - record->m_opcodeSampleCount, record->m_sampleCount, (static_cast<double>(record->m_sampleCount - record->m_opcodeSampleCount) * 100) / record->m_sampleCount); + dataLogF("\n"); + dataLogF(" [*] Samples inside host code are charged to the calling Bytecode.\n"); + dataLogF(" Samples on a call / return boundary are not charged to a specific opcode or line.\n\n"); + dataLogF(" Samples on a call / return boundary: %d / %d (%.3f%%)\n\n", record->m_sampleCount - record->m_opcodeSampleCount, record->m_sampleCount, (static_cast<double>(record->m_sampleCount - record->m_opcodeSampleCount) * 100) / record->m_sampleCount); } } } diff --git a/Source/JavaScriptCore/bytecode/SpeculatedType.h b/Source/JavaScriptCore/bytecode/SpeculatedType.h index 09ba9fdfa..656bc79ee 100644 --- a/Source/JavaScriptCore/bytecode/SpeculatedType.h +++ b/Source/JavaScriptCore/bytecode/SpeculatedType.h @@ -61,6 +61,7 @@ static const SpeculatedType SpecInt32 = 0x00800000; // It's definite static const SpeculatedType SpecDoubleReal = 0x01000000; // It's definitely a non-NaN double. static const SpeculatedType SpecDoubleNaN = 0x02000000; // It's definitely a NaN. static const SpeculatedType SpecDouble = 0x03000000; // It's either a non-NaN or a NaN double. +static const SpeculatedType SpecRealNumber = 0x01800000; // It's either an Int32 or a DoubleReal. static const SpeculatedType SpecNumber = 0x03800000; // It's either an Int32 or a Double. static const SpeculatedType SpecBoolean = 0x04000000; // It's definitely a Boolean. static const SpeculatedType SpecOther = 0x08000000; // It's definitely none of the above. @@ -228,6 +229,16 @@ inline bool isInt32Speculation(SpeculatedType value) return value == SpecInt32; } +inline bool isInt32SpeculationForArithmetic(SpeculatedType value) +{ + return !(value & SpecDouble); +} + +inline bool isInt32SpeculationExpectingDefined(SpeculatedType value) +{ + return isInt32Speculation(value & ~SpecOther); +} + inline bool isDoubleRealSpeculation(SpeculatedType value) { return value == SpecDoubleReal; @@ -238,11 +249,26 @@ inline bool isDoubleSpeculation(SpeculatedType value) return !!value && (value & SpecDouble) == value; } +inline bool isDoubleSpeculationForArithmetic(SpeculatedType value) +{ + return !!(value & SpecDouble); +} + +inline bool isRealNumberSpeculation(SpeculatedType value) +{ + return !!(value & SpecRealNumber) && !(value & ~SpecRealNumber); +} + inline bool isNumberSpeculation(SpeculatedType value) { return !!(value & SpecNumber) && !(value & ~SpecNumber); } +inline bool isNumberSpeculationExpectingDefined(SpeculatedType value) +{ + return isNumberSpeculation(value & ~SpecOther); +} + inline bool isBooleanSpeculation(SpeculatedType value) { return value == SpecBoolean; diff --git a/Source/JavaScriptCore/bytecode/StructureStubInfo.h b/Source/JavaScriptCore/bytecode/StructureStubInfo.h index a1bbc9c37..445ffe6c6 100644 --- a/Source/JavaScriptCore/bytecode/StructureStubInfo.h +++ b/Source/JavaScriptCore/bytecode/StructureStubInfo.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp index 8aa48404a..e98d4de0a 100644 --- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp +++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp @@ -80,8 +80,6 @@ void UnlinkedFunctionExecutable::visitChildren(JSCell* cell, SlotVisitor& visito COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); Base::visitChildren(thisObject, visitor); - visitor.append(&thisObject->m_codeBlockForCall); - visitor.append(&thisObject->m_codeBlockForConstruct); visitor.append(&thisObject->m_nameValue); visitor.append(&thisObject->m_symbolTableForCall); visitor.append(&thisObject->m_symbolTableForConstruct); @@ -112,12 +110,16 @@ UnlinkedFunctionCodeBlock* UnlinkedFunctionExecutable::codeBlockFor(JSGlobalData { switch (specializationKind) { case CodeForCall: - if (m_codeBlockForCall) - return m_codeBlockForCall.get(); + if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForCall.get()) { + globalData.codeCache()->usedFunctionCode(globalData, codeBlock); + return codeBlock; + } break; case CodeForConstruct: - if (m_codeBlockForConstruct) - return m_codeBlockForConstruct.get(); + if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForConstruct.get()) { + globalData.codeCache()->usedFunctionCode(globalData, codeBlock); + return codeBlock; + } break; } @@ -128,11 +130,11 @@ UnlinkedFunctionCodeBlock* UnlinkedFunctionExecutable::codeBlockFor(JSGlobalData switch (specializationKind) { case CodeForCall: - m_codeBlockForCall.set(globalData, this, result); + m_codeBlockForCall = PassWeak<UnlinkedFunctionCodeBlock>(result); m_symbolTableForCall.set(globalData, this, result->symbolTable()); break; case CodeForConstruct: - m_codeBlockForConstruct.set(globalData, this, result); + m_codeBlockForConstruct = PassWeak<UnlinkedFunctionCodeBlock>(result); m_symbolTableForConstruct.set(globalData, this, result->symbolTable()); break; } @@ -171,6 +173,7 @@ UnlinkedCodeBlock::UnlinkedCodeBlock(JSGlobalData* globalData, Structure* struct , m_resolveOperationCount(0) , m_putToBaseOperationCount(1) , m_arrayProfileCount(0) + , m_arrayAllocationProfileCount(0) , m_valueProfileCount(0) , m_llintCallLinkInfoCount(0) #if ENABLE(BYTECODE_COMMENTS) diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h index bf3f5fdff..23937d773 100644 --- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h +++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h @@ -36,6 +36,7 @@ #include "Nodes.h" #include "RegExp.h" #include "SpecialPointer.h" +#include "Weak.h" #include <wtf/RefCountedArray.h> #include <wtf/Vector.h> @@ -56,6 +57,7 @@ class UnlinkedFunctionCodeBlock; typedef unsigned UnlinkedValueProfile; typedef unsigned UnlinkedArrayProfile; +typedef unsigned UnlinkedArrayAllocationProfile; typedef unsigned UnlinkedLLIntCallLinkInfo; struct ExecutableInfo { @@ -107,7 +109,7 @@ public: FunctionExecutable* link(JSGlobalData&, const SourceCode&, size_t lineOffset, size_t sourceOffset); - void clearCode() + void clearCodeForRecompilation() { m_symbolTableForCall.clear(); m_symbolTableForConstruct.clear(); @@ -135,8 +137,8 @@ public: private: UnlinkedFunctionExecutable(JSGlobalData*, Structure*, const SourceCode&, FunctionBodyNode*); - WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForCall; - WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForConstruct; + Weak<UnlinkedFunctionCodeBlock> m_codeBlockForCall; + Weak<UnlinkedFunctionCodeBlock> m_codeBlockForConstruct; unsigned m_numCapturedVariables : 29; bool m_forceUsesArguments : 1; @@ -392,6 +394,8 @@ public: UnlinkedArrayProfile addArrayProfile() { return m_arrayProfileCount++; } unsigned numberOfArrayProfiles() { return m_arrayProfileCount; } + UnlinkedArrayAllocationProfile addArrayAllocationProfile() { return m_arrayAllocationProfileCount++; } + unsigned numberOfArrayAllocationProfiles() { return m_arrayAllocationProfileCount; } UnlinkedValueProfile addValueProfile() { return m_valueProfileCount++; } unsigned numberOfValueProfiles() { return m_valueProfileCount; } @@ -518,6 +522,7 @@ private: unsigned m_resolveOperationCount; unsigned m_putToBaseOperationCount; unsigned m_arrayProfileCount; + unsigned m_arrayAllocationProfileCount; unsigned m_valueProfileCount; unsigned m_llintCallLinkInfoCount; diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp index b11872551..c6f81f3c3 100644 --- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp +++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp @@ -457,8 +457,16 @@ BytecodeGenerator::BytecodeGenerator(JSGlobalData& globalData, FunctionBodyNode* if (isConstructor()) { prependComment("'this' because we are a Constructor function"); - emitOpcode(op_create_this); - instructions().append(m_thisRegister.index()); + + RefPtr<RegisterID> func = newTemporary(); + + UnlinkedValueProfile profile = emitProfiledOpcode(op_get_callee); + instructions().append(func->index()); + instructions().append(profile); + + emitOpcode(op_create_this); + instructions().append(m_thisRegister.index()); + instructions().append(func->index()); } else if (!codeBlock->isStrictMode() && (functionBody->usesThis() || codeBlock->usesEval() || m_shouldEmitDebugHooks)) { UnlinkedValueProfile profile = emitProfiledOpcode(op_convert_this); instructions().append(m_thisRegister.index()); @@ -716,6 +724,15 @@ UnlinkedArrayProfile BytecodeGenerator::newArrayProfile() #endif } +UnlinkedArrayAllocationProfile BytecodeGenerator::newArrayAllocationProfile() +{ +#if ENABLE(VALUE_PROFILER) + return m_codeBlock->addArrayAllocationProfile(); +#else + return 0; +#endif +} + UnlinkedValueProfile BytecodeGenerator::emitProfiledOpcode(OpcodeID opcodeID) { #if ENABLE(VALUE_PROFILER) @@ -1605,6 +1622,7 @@ RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elemen instructions().append(dst->index()); instructions().append(constantBufferIndex); instructions().append(length); + instructions().append(newArrayAllocationProfile()); return dst; } } @@ -1622,6 +1640,7 @@ RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elemen instructions().append(dst->index()); instructions().append(argv.size() ? argv[0]->index() : 0); // argv instructions().append(argv.size()); // argc + instructions().append(newArrayAllocationProfile()); return dst; } @@ -1757,12 +1776,14 @@ ExpectedFunction BytecodeGenerator::emitExpectedFunctionSnippet(RegisterID* dst, emitOpcode(op_new_array_with_size); instructions().append(dst->index()); instructions().append(callArguments.argumentRegister(0)->index()); + instructions().append(newArrayAllocationProfile()); } else { ASSERT(callArguments.argumentCountIncludingThis() == 1); emitOpcode(op_new_array); instructions().append(dst->index()); instructions().append(0); instructions().append(0); + instructions().append(newArrayAllocationProfile()); } } break; diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h index 828726dee..2e7aa2035 100644 --- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h +++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h @@ -523,6 +523,7 @@ namespace JSC { #endif void emitOpcode(OpcodeID); + UnlinkedArrayAllocationProfile newArrayAllocationProfile(); UnlinkedArrayProfile newArrayProfile(); UnlinkedValueProfile emitProfiledOpcode(OpcodeID); void retrieveLastBinaryOp(int& dstIndex, int& src1Index, int& src2Index); diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp index 85da1f4eb..71b6c7cac 100644 --- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp +++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp @@ -1035,6 +1035,16 @@ RegisterID* BinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); RegisterID* src2 = generator.emitNode(m_expr2); + if (generator.m_lastOpcodeID == op_typeof && (opcodeID == op_neq || opcodeID == op_nstricteq)) { + RefPtr<RegisterID> tmp = generator.tempDestination(dst); + if (opcodeID == op_neq) + generator.emitEqualityOp(op_eq, generator.finalDestination(tmp.get(), src1.get()), src1.get(), src2); + else if (opcodeID == op_nstricteq) + generator.emitEqualityOp(op_stricteq, generator.finalDestination(tmp.get(), src1.get()), src1.get(), src2); + else + ASSERT_NOT_REACHED(); + return generator.emitUnaryOp(op_not, generator.finalDestination(dst, tmp.get()), tmp.get()); + } return generator.emitBinaryOp(opcodeID, generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); } diff --git a/Source/JavaScriptCore/debugger/Debugger.cpp b/Source/JavaScriptCore/debugger/Debugger.cpp index b14729146..7eda52dc8 100644 --- a/Source/JavaScriptCore/debugger/Debugger.cpp +++ b/Source/JavaScriptCore/debugger/Debugger.cpp @@ -80,7 +80,7 @@ inline void Recompiler::operator()(JSCell* cell) ExecState* exec = function->scope()->globalObject()->JSGlobalObject::globalExec(); executable->clearCodeIfNotCompiling(); - executable->clearUnlinkedCodeIfNotCompiling(); + executable->clearUnlinkedCodeForRecompilationIfNotCompiling(); if (m_debugger == function->scope()->globalObject()->debugger()) m_sourceProviders.add(executable->source().provider(), exec); } diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp index e518c24a8..23b84cedf 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp @@ -30,6 +30,8 @@ #include "CodeBlock.h" #include "DFGBasicBlock.h" +#include "GetByIdStatus.h" +#include "PutByIdStatus.h" namespace JSC { namespace DFG { @@ -150,16 +152,16 @@ void AbstractState::initialize(Graph& graph) int operand = graph.m_mustHandleValues.operandForIndex(i); block->valuesAtHead.operand(operand).merge(value); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Initializing Block #%u, operand r%d, to ", blockIndex, operand); + dataLogF(" Initializing Block #%u, operand r%d, to ", blockIndex, operand); block->valuesAtHead.operand(operand).dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif } block->cfaShouldRevisit = true; } } -bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDirectionPtr) +bool AbstractState::endBasicBlock(MergeMode mergeMode) { ASSERT(m_block); @@ -167,6 +169,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi block->cfaFoundConstants = m_foundConstants; block->cfaDidFinish = m_isValid; + block->cfaBranchDirection = m_branchDirection; if (!m_isValid) { reset(); @@ -178,7 +181,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi if (mergeMode != DontMerge || !ASSERT_DISABLED) { for (size_t argument = 0; argument < block->variablesAtTail.numberOfArguments(); ++argument) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Merging state for argument %zu.\n", argument); + dataLogF(" Merging state for argument %zu.\n", argument); #endif AbstractValue& destination = block->valuesAtTail.argument(argument); changed |= mergeStateAtTail(destination, m_variables.argument(argument), block->variablesAtTail.argument(argument)); @@ -186,7 +189,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi for (size_t local = 0; local < block->variablesAtTail.numberOfLocals(); ++local) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Merging state for local %zu.\n", local); + dataLogF(" Merging state for local %zu.\n", local); #endif AbstractValue& destination = block->valuesAtTail.local(local); changed |= mergeStateAtTail(destination, m_variables.local(local), block->variablesAtTail.local(local)); @@ -195,12 +198,8 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi ASSERT(mergeMode != DontMerge || !changed); - BranchDirection branchDirection = m_branchDirection; - if (branchDirectionPtr) - *branchDirectionPtr = branchDirection; - #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Branch direction = %s\n", branchDirectionToString(branchDirection)); + dataLogF(" Branch direction = %s\n", branchDirectionToString(m_branchDirection)); #endif reset(); @@ -208,7 +207,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi if (mergeMode != MergeToSuccessors) return changed; - return mergeToSuccessors(m_graph, block, branchDirection); + return mergeToSuccessors(m_graph, block); } void AbstractState::reset() @@ -218,6 +217,27 @@ void AbstractState::reset() m_branchDirection = InvalidBranchDirection; } +AbstractState::BooleanResult AbstractState::booleanResult(Node& node, AbstractValue& value) +{ + JSValue childConst = value.value(); + if (childConst) { + if (childConst.toBoolean(m_codeBlock->globalObjectFor(node.codeOrigin)->globalExec())) + return DefinitelyTrue; + return DefinitelyFalse; + } + + // Next check if we can fold because we know that the source is an object or string and does not equal undefined. + if (isCellSpeculation(value.m_type) + && value.m_currentKnownStructure.hasSingleton()) { + Structure* structure = value.m_currentKnownStructure.singleton(); + if (!structure->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node.codeOrigin)) + && structure->typeInfo().type() != StringType) + return DefinitelyTrue; + } + + return UnknownBooleanResult; +} + bool AbstractState::execute(unsigned indexInBlock) { ASSERT(m_block); @@ -239,6 +259,12 @@ bool AbstractState::execute(unsigned indexInBlock) node.setCanExit(false); break; } + + case Identity: { + forNode(nodeIndex) = forNode(node.child1()); + node.setCanExit(false); + break; + } case GetLocal: { VariableAccessData* variableAccessData = node.variableAccessData(); @@ -424,7 +450,10 @@ bool AbstractState::execute(unsigned indexInBlock) break; } speculateNumberUnary(node); - forNode(nodeIndex).set(SpecDouble); + if (isInt32Speculation(forNode(node.child1()).m_type)) + forNode(nodeIndex).set(SpecDoubleReal); + else + forNode(nodeIndex).set(SpecDouble); break; } @@ -448,9 +477,13 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(nodeIndex).set(SpecInt32); break; } - if (Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()])) { + if (Node::shouldSpeculateNumberExpectingDefined(m_graph[node.child1()], m_graph[node.child2()])) { speculateNumberBinary(node); - forNode(nodeIndex).set(SpecDouble); + if (isRealNumberSpeculation(forNode(node.child1()).m_type) + && isRealNumberSpeculation(forNode(node.child2()).m_type)) + forNode(nodeIndex).set(SpecDoubleReal); + else + forNode(nodeIndex).set(SpecDouble); break; } if (node.op() == ValueAdd) { @@ -522,7 +555,11 @@ bool AbstractState::execute(unsigned indexInBlock) break; } speculateNumberBinary(node); - forNode(nodeIndex).set(SpecDouble); + if (isRealNumberSpeculation(forNode(node.child1()).m_type) + || isRealNumberSpeculation(forNode(node.child2()).m_type)) + forNode(nodeIndex).set(SpecDoubleReal); + else + forNode(nodeIndex).set(SpecDouble); break; } @@ -560,7 +597,7 @@ bool AbstractState::execute(unsigned indexInBlock) break; } } - if (Node::shouldSpeculateInteger( + if (Node::shouldSpeculateIntegerForArithmetic( m_graph[node.child1()], m_graph[node.child2()]) && node.canSpeculateInteger()) { speculateInt32Binary(node, true); // forcing can-exit, which is a bit on the conservative side. @@ -580,7 +617,7 @@ bool AbstractState::execute(unsigned indexInBlock) node.setCanExit(false); break; } - if (m_graph[node.child1()].shouldSpeculateInteger() + if (m_graph[node.child1()].shouldSpeculateIntegerForArithmetic() && node.canSpeculateInteger()) { speculateInt32Unary(node, true); forNode(nodeIndex).set(SpecInt32); @@ -605,8 +642,18 @@ bool AbstractState::execute(unsigned indexInBlock) } case LogicalNot: { - JSValue childConst = forNode(node.child1()).value(); - if (childConst && trySetConstant(nodeIndex, jsBoolean(!childConst.toBoolean(m_codeBlock->globalObjectFor(node.codeOrigin)->globalExec())))) { + bool didSetConstant = false; + switch (booleanResult(node, forNode(node.child1()))) { + case DefinitelyTrue: + didSetConstant = trySetConstant(nodeIndex, jsBoolean(false)); + break; + case DefinitelyFalse: + didSetConstant = trySetConstant(nodeIndex, jsBoolean(true)); + break; + default: + break; + } + if (didSetConstant) { m_foundConstants = true; node.setCanExit(false); break; @@ -678,12 +725,13 @@ bool AbstractState::execute(unsigned indexInBlock) case CompareGreater: case CompareGreaterEq: case CompareEq: { + bool constantWasSet = false; + JSValue leftConst = forNode(node.child1()).value(); JSValue rightConst = forNode(node.child2()).value(); if (leftConst && rightConst && leftConst.isNumber() && rightConst.isNumber()) { double a = leftConst.asNumber(); double b = rightConst.asNumber(); - bool constantWasSet; switch (node.op()) { case CompareLess: constantWasSet = trySetConstant(nodeIndex, jsBoolean(a < b)); @@ -705,11 +753,20 @@ bool AbstractState::execute(unsigned indexInBlock) constantWasSet = false; break; } - if (constantWasSet) { - m_foundConstants = true; - node.setCanExit(false); - break; - } + } + + if (!constantWasSet && node.op() == CompareEq) { + SpeculatedType leftType = forNode(node.child1()).m_type; + SpeculatedType rightType = forNode(node.child2()).m_type; + if ((isInt32Speculation(leftType) && isOtherSpeculation(rightType)) + || (isOtherSpeculation(leftType) && isInt32Speculation(rightType))) + constantWasSet = trySetConstant(nodeIndex, jsBoolean(false)); + } + + if (constantWasSet) { + m_foundConstants = true; + node.setCanExit(false); + break; } forNode(nodeIndex).set(SpecBoolean); @@ -842,6 +899,7 @@ bool AbstractState::execute(unsigned indexInBlock) switch (node.arrayMode().type()) { case Array::SelectUsingPredictions: case Array::Unprofiled: + case Array::Undecided: ASSERT_NOT_REACHED(); break; case Array::ForceExit: @@ -859,6 +917,24 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).makeTop(); break; + case Array::Int32: + forNode(node.child2()).filter(SpecInt32); + if (node.arrayMode().isOutOfBounds()) { + clobberWorld(node.codeOrigin, indexInBlock); + forNode(nodeIndex).makeTop(); + } else + forNode(nodeIndex).set(SpecInt32); + break; + case Array::Double: + forNode(node.child2()).filter(SpecInt32); + if (node.arrayMode().isOutOfBounds()) { + clobberWorld(node.codeOrigin, indexInBlock); + forNode(nodeIndex).makeTop(); + } else if (node.arrayMode().isSaneChain()) + forNode(nodeIndex).set(SpecDouble); + else + forNode(nodeIndex).set(SpecDoubleReal); + break; case Array::Contiguous: case Array::ArrayStorage: case Array::SlowPutArrayStorage: @@ -926,6 +1002,20 @@ bool AbstractState::execute(unsigned indexInBlock) case Array::Generic: clobberWorld(node.codeOrigin, indexInBlock); break; + case Array::Int32: + forNode(child1).filter(SpecCell); + forNode(child2).filter(SpecInt32); + forNode(child3).filter(SpecInt32); + if (node.arrayMode().isOutOfBounds()) + clobberWorld(node.codeOrigin, indexInBlock); + break; + case Array::Double: + forNode(child1).filter(SpecCell); + forNode(child2).filter(SpecInt32); + forNode(child3).filter(SpecRealNumber); + if (node.arrayMode().isOutOfBounds()) + clobberWorld(node.codeOrigin, indexInBlock); + break; case Array::Contiguous: case Array::ArrayStorage: forNode(child1).filter(SpecCell); @@ -1018,6 +1108,16 @@ bool AbstractState::execute(unsigned indexInBlock) case ArrayPush: node.setCanExit(true); + switch (node.arrayMode().type()) { + case Array::Int32: + forNode(node.child2()).filter(SpecInt32); + break; + case Array::Double: + forNode(node.child2()).filter(SpecRealNumber); + break; + default: + break; + } clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).set(SpecNumber); break; @@ -1043,23 +1143,21 @@ bool AbstractState::execute(unsigned indexInBlock) break; case Branch: { - JSValue value = forNode(node.child1()).value(); - if (value) { - bool booleanValue = value.toBoolean(m_codeBlock->globalObjectFor(node.codeOrigin)->globalExec()); - if (booleanValue) - m_branchDirection = TakeTrue; - else - m_branchDirection = TakeFalse; + BooleanResult result = booleanResult(node, forNode(node.child1())); + if (result == DefinitelyTrue) { + m_branchDirection = TakeTrue; + node.setCanExit(false); + break; + } + if (result == DefinitelyFalse) { + m_branchDirection = TakeFalse; node.setCanExit(false); break; } // FIXME: The above handles the trivial cases of sparse conditional // constant propagation, but we can do better: - // 1) If the abstract value does not have a concrete value but describes - // something that is known to evaluate true (or false) then we ought - // to sparse conditional that. - // 2) We can specialize the source variable's value on each direction of - // the branch. + // We can specialize the source variable's value on each direction of + // the branch. Node& child = m_graph[node.child1()]; if (child.shouldSpeculateBoolean()) speculateBooleanUnary(node); @@ -1122,13 +1220,13 @@ bool AbstractState::execute(unsigned indexInBlock) case NewArray: node.setCanExit(true); - forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructure()); + forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())); m_haveStructures = true; break; case NewArrayBuffer: node.setCanExit(true); - forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructure()); + forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())); m_haveStructures = true; break; @@ -1156,6 +1254,7 @@ bool AbstractState::execute(unsigned indexInBlock) // be hit, but then again, you never know. destination = source; node.setCanExit(false); + m_foundConstants = true; // Tell the constant folder to turn this into Identity. break; } @@ -1188,10 +1287,14 @@ bool AbstractState::execute(unsigned indexInBlock) destination.set(SpecFinalObject); break; } + + case InheritorIDWatchpoint: + node.setCanExit(true); + break; case NewObject: node.setCanExit(false); - forNode(nodeIndex).set(m_codeBlock->globalObjectFor(node.codeOrigin)->emptyObjectStructure()); + forNode(nodeIndex).set(node.structure()); m_haveStructures = true; break; @@ -1308,8 +1411,30 @@ bool AbstractState::execute(unsigned indexInBlock) m_isValid = false; break; } - if (isCellSpeculation(m_graph[node.child1()].prediction())) + if (isCellSpeculation(m_graph[node.child1()].prediction())) { forNode(node.child1()).filter(SpecCell); + + if (Structure* structure = forNode(node.child1()).bestProvenStructure()) { + GetByIdStatus status = GetByIdStatus::computeFor( + m_graph.m_globalData, structure, + m_graph.m_codeBlock->identifier(node.identifierNumber())); + if (status.isSimple()) { + // Assert things that we can't handle and that the computeFor() method + // above won't be able to return. + ASSERT(status.structureSet().size() == 1); + ASSERT(status.chain().isEmpty()); + + if (status.specificValue()) + forNode(nodeIndex).set(status.specificValue()); + else + forNode(nodeIndex).makeTop(); + forNode(node.child1()).filter(status.structureSet()); + + m_foundConstants = true; + break; + } + } + } clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).makeTop(); break; @@ -1374,7 +1499,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(nodeIndex).clear(); // The result is not a JS value. break; case CheckArray: { - if (node.arrayMode().alreadyChecked(forNode(node.child1()))) { + if (node.arrayMode().alreadyChecked(m_graph, node, forNode(node.child1()))) { m_foundConstants = true; node.setCanExit(false); break; @@ -1384,11 +1509,11 @@ bool AbstractState::execute(unsigned indexInBlock) case Array::String: forNode(node.child1()).filter(SpecString); break; + case Array::Int32: + case Array::Double: 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); break; case Array::Arguments: @@ -1430,7 +1555,7 @@ bool AbstractState::execute(unsigned indexInBlock) break; } case Arrayify: { - if (node.arrayMode().alreadyChecked(forNode(node.child1()))) { + if (node.arrayMode().alreadyChecked(m_graph, node, forNode(node.child1()))) { m_foundConstants = true; node.setCanExit(false); break; @@ -1472,25 +1597,65 @@ bool AbstractState::execute(unsigned indexInBlock) break; } case GetByOffset: - node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecCell); + if (!m_graph[node.child1()].hasStorageResult()) { + node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type)); + forNode(node.child1()).filter(SpecCell); + } forNode(nodeIndex).makeTop(); break; - case PutByOffset: - node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecCell); + case PutByOffset: { + bool canExit = false; + if (!m_graph[node.child1()].hasStorageResult()) { + canExit |= !isCellSpeculation(forNode(node.child1()).m_type); + forNode(node.child1()).filter(SpecCell); + } + canExit |= !isCellSpeculation(forNode(node.child2()).m_type); + forNode(node.child2()).filter(SpecCell); + node.setCanExit(canExit); break; + } - case CheckFunction: + case CheckFunction: { + JSValue value = forNode(node.child1()).value(); + if (value == node.function()) { + m_foundConstants = true; + ASSERT(value); + node.setCanExit(false); + break; + } + node.setCanExit(true); // Lies! We can do better. - forNode(node.child1()).filter(SpecFunction); - // FIXME: Should be able to propagate the fact that we know what the function is. + if (!forNode(node.child1()).filterByValue(node.function())) { + m_isValid = false; + break; + } break; + } case PutById: case PutByIdDirect: node.setCanExit(true); + if (Structure* structure = forNode(node.child1()).bestProvenStructure()) { + PutByIdStatus status = PutByIdStatus::computeFor( + m_graph.m_globalData, + m_graph.globalObjectFor(node.codeOrigin), + structure, + m_graph.m_codeBlock->identifier(node.identifierNumber()), + node.op() == PutByIdDirect); + if (status.isSimpleReplace()) { + forNode(node.child1()).filter(structure); + m_foundConstants = true; + break; + } + if (status.isSimpleTransition()) { + clobberStructures(indexInBlock); + forNode(node.child1()).set(status.newStructure()); + m_haveStructures = true; + m_foundConstants = true; + break; + } + } forNode(node.child1()).filter(SpecCell); clobberWorld(node.codeOrigin, indexInBlock); break; @@ -1622,15 +1787,15 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract return false; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" It's live, node @%u.\n", nodeIndex); + dataLogF(" It's live, node @%u.\n", nodeIndex); #endif if (node.variableAccessData()->isCaptured()) { source = inVariable; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Transfering "); + dataLogF(" Transfering "); source.dump(WTF::dataFile()); - dataLog(" from last access due to captured variable.\n"); + dataLogF(" from last access due to captured variable.\n"); #endif } else { switch (node.op()) { @@ -1640,9 +1805,9 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract // The block transfers the value from head to tail. source = inVariable; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Transfering "); + dataLogF(" Transfering "); source.dump(WTF::dataFile()); - dataLog(" from head to tail.\n"); + dataLogF(" from head to tail.\n"); #endif break; @@ -1650,9 +1815,9 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract // The block refines the value with additional speculations. source = forNode(nodeIndex); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Refining to "); + dataLogF(" Refining to "); source.dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif break; @@ -1665,9 +1830,9 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract } else source = forNode(node.child1()); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Setting to "); + dataLogF(" Setting to "); source.dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif break; @@ -1681,7 +1846,7 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract // Abstract execution did not change the output value of the variable, for this // basic block, on this iteration. #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Not changed!\n"); + dataLogF(" Not changed!\n"); #endif return false; } @@ -1691,7 +1856,7 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract // true to indicate that the fixpoint must go on! destination = source; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Changed!\n"); + dataLogF(" Changed!\n"); #endif return true; } @@ -1722,7 +1887,7 @@ inline bool AbstractState::merge(BasicBlock* from, BasicBlock* to) } inline bool AbstractState::mergeToSuccessors( - Graph& graph, BasicBlock* basicBlock, BranchDirection branchDirection) + Graph& graph, BasicBlock* basicBlock) { Node& terminal = graph[basicBlock->last()]; @@ -1730,25 +1895,25 @@ inline bool AbstractState::mergeToSuccessors( switch (terminal.op()) { case Jump: { - ASSERT(branchDirection == InvalidBranchDirection); + ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Merging to block #%u.\n", terminal.takenBlockIndex()); + dataLogF(" Merging to block #%u.\n", terminal.takenBlockIndex()); #endif return merge(basicBlock, graph.m_blocks[terminal.takenBlockIndex()].get()); } case Branch: { - ASSERT(branchDirection != InvalidBranchDirection); + ASSERT(basicBlock->cfaBranchDirection != InvalidBranchDirection); bool changed = false; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Merging to block #%u.\n", terminal.takenBlockIndex()); + dataLogF(" Merging to block #%u.\n", terminal.takenBlockIndex()); #endif - if (branchDirection != TakeFalse) + if (basicBlock->cfaBranchDirection != TakeFalse) changed |= merge(basicBlock, graph.m_blocks[terminal.takenBlockIndex()].get()); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Merging to block #%u.\n", terminal.notTakenBlockIndex()); + dataLogF(" Merging to block #%u.\n", terminal.notTakenBlockIndex()); #endif - if (branchDirection != TakeTrue) + if (basicBlock->cfaBranchDirection != TakeTrue) changed |= merge(basicBlock, graph.m_blocks[terminal.notTakenBlockIndex()].get()); return changed; } @@ -1756,7 +1921,7 @@ inline bool AbstractState::mergeToSuccessors( case Return: case Throw: case ThrowReferenceError: - ASSERT(branchDirection == InvalidBranchDirection); + ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection); return false; default: diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.h b/Source/JavaScriptCore/dfg/DFGAbstractState.h index ec1a06231..230cd836c 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,6 +31,7 @@ #if ENABLE(DFG_JIT) #include "DFGAbstractValue.h" +#include "DFGBranchDirection.h" #include "DFGGraph.h" #include "DFGNode.h" #include <wtf/Vector.h> @@ -92,36 +93,6 @@ public: MergeToSuccessors }; - enum BranchDirection { - // This is not a branch and so there is no branch direction, or - // the branch direction has yet to be set. - InvalidBranchDirection, - - // The branch takes the true case. - TakeTrue, - - // The branch takes the false case. - TakeFalse, - - // For all we know, the branch could go either direction, so we - // have to assume the worst. - TakeBoth - }; - - static const char* branchDirectionToString(BranchDirection branchDirection) - { - switch (branchDirection) { - case InvalidBranchDirection: - return "Invalid"; - case TakeTrue: - return "TakeTrue"; - case TakeFalse: - return "TakeFalse"; - case TakeBoth: - return "TakeBoth"; - } - } - AbstractState(Graph&); ~AbstractState(); @@ -174,11 +145,7 @@ public: // A true return means that you must revisit (at least) the successor // blocks. This also sets cfaShouldRevisit to true for basic blocks // that must be visited next. - // - // If you'd like to know what direction the branch at the end of the - // basic block is thought to have taken, you can pass a non-0 pointer - // for BranchDirection. - bool endBasicBlock(MergeMode, BranchDirection* = 0); + bool endBasicBlock(MergeMode); // Reset the AbstractState. This throws away any results, and at this point // you can safely call beginBasicBlock() on any basic block. @@ -211,8 +178,8 @@ public: // successors. Returns true if any of the successors' states changed. Note // that this is automatically called in endBasicBlock() if MergeMode is // MergeToSuccessors. - bool mergeToSuccessors(Graph&, BasicBlock*, BranchDirection); - + bool mergeToSuccessors(Graph&, BasicBlock*); + void dump(FILE* out); private: @@ -268,6 +235,13 @@ private: childValue2.filter(SpecNumber); } + enum BooleanResult { + UnknownBooleanResult, + DefinitelyFalse, + DefinitelyTrue + }; + BooleanResult booleanResult(Node&, AbstractValue&); + bool trySetConstant(NodeIndex nodeIndex, JSValue value) { // Make sure we don't constant fold something that will produce values that contravene diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h index c198b5e52..c60b792f6 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractValue.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.h @@ -284,6 +284,21 @@ struct AbstractValue { checkConsistency(); } + bool filterByValue(JSValue value) + { + if (!validate(value)) + return false; + + if (!!value && value.isCell()) + filter(StructureSet(value.asCell()->structure())); + else + filter(speculationFromValue(value)); + + m_value = value; + + return true; + } + bool validateType(JSValue value) const { if (isTop()) @@ -327,6 +342,15 @@ struct AbstractValue { return true; } + Structure* bestProvenStructure() const + { + if (m_currentKnownStructure.hasSingleton()) + return m_currentKnownStructure.singleton(); + if (m_futurePossibleStructure.hasSingleton()) + return m_futurePossibleStructure.singleton(); + return 0; + } + void checkConsistency() const { if (!(m_type & SpecCell)) { @@ -351,7 +375,7 @@ struct AbstractValue { { fprintf(out, "(%s, %s, ", speculationToString(m_type), arrayModesToString(m_arrayModes)); m_currentKnownStructure.dump(out); - dataLog(", "); + dataLogF(", "); m_futurePossibleStructure.dump(out); if (!!m_value) fprintf(out, ", %s", m_value.description()); diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp index 00b1109f6..b02e0112c 100644 --- a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp @@ -359,49 +359,49 @@ public: } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Arguments aliasing states:\n"); + dataLogF("Arguments aliasing states:\n"); for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) { VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i]; if (!variableAccessData->isRoot()) continue; - dataLog(" r%d(%s): ", variableAccessData->local(), m_graph.nameOfVariableAccessData(variableAccessData)); + dataLogF(" r%d(%s): ", variableAccessData->local(), m_graph.nameOfVariableAccessData(variableAccessData)); if (variableAccessData->isCaptured()) - dataLog("Captured"); + dataLogF("Captured"); else { ArgumentsAliasingData& data = m_argumentsAliasing.find(variableAccessData)->value; bool first = true; if (data.callContextIsValid()) { if (!first) - dataLog(", "); - dataLog("Have Call Context: %p", data.callContext); + dataLogF(", "); + dataLogF("Have Call Context: %p", data.callContext); first = false; if (!m_createsArguments.contains(data.callContext)) - dataLog(" (Does Not Create Arguments)"); + dataLogF(" (Does Not Create Arguments)"); } if (data.argumentsAssignmentIsValid()) { if (!first) - dataLog(", "); - dataLog("Arguments Assignment Is Valid"); + dataLogF(", "); + dataLogF("Arguments Assignment Is Valid"); first = false; } if (!data.escapes) { if (!first) - dataLog(", "); - dataLog("Does Not Escape"); + dataLogF(", "); + dataLogF("Does Not Escape"); first = false; } if (!first) - dataLog(", "); + dataLogF(", "); if (data.isValid()) { if (m_createsArguments.contains(data.callContext)) - dataLog("VALID"); + dataLogF("VALID"); else - dataLog("INVALID (due to argument creation)"); + dataLogF("INVALID (due to argument creation)"); } else - dataLog("INVALID (due to bad variable use)"); + dataLogF("INVALID (due to bad variable use)"); } - dataLog("\n"); + dataLogF("\n"); } #endif diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp index 699902a16..3bfb6a43e 100644 --- a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp +++ b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp @@ -29,24 +29,52 @@ #if ENABLE(DFG_JIT) #include "DFGAbstractValue.h" +#include "DFGGraph.h" namespace JSC { namespace DFG { ArrayMode ArrayMode::fromObserved(ArrayProfile* profile, Array::Action action, bool makeSafe) { - switch (profile->observedArrayModes()) { + ArrayModes observed = profile->observedArrayModes(); + switch (observed) { case 0: return ArrayMode(Array::Unprofiled); case asArrayModes(NonArray): if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) - 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::Undecided, Array::NonArray, Array::OutOfBounds, Array::Convert); return ArrayMode(Array::SelectUsingPredictions); + + case asArrayModes(ArrayWithUndecided): + if (action == Array::Write) + return ArrayMode(Array::Undecided, Array::Array, Array::OutOfBounds, Array::Convert); + return ArrayMode(Array::Generic); + + case asArrayModes(NonArray) | asArrayModes(ArrayWithUndecided): + if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) + return ArrayMode(Array::Undecided, Array::PossiblyArray, Array::OutOfBounds, Array::Convert); + return ArrayMode(Array::SelectUsingPredictions); + + case asArrayModes(NonArrayWithInt32): + return ArrayMode(Array::Int32, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe); + case asArrayModes(ArrayWithInt32): + return ArrayMode(Array::Int32, Array::Array, Array::AsIs).withProfile(profile, makeSafe); + case asArrayModes(NonArrayWithInt32) | asArrayModes(ArrayWithInt32): + return ArrayMode(Array::Int32, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe); + + case asArrayModes(NonArrayWithDouble): + return ArrayMode(Array::Double, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe); + case asArrayModes(ArrayWithDouble): + return ArrayMode(Array::Double, Array::Array, Array::AsIs).withProfile(profile, makeSafe); + case asArrayModes(NonArrayWithDouble) | asArrayModes(ArrayWithDouble): + return ArrayMode(Array::Double, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe); + case asArrayModes(NonArrayWithContiguous): return ArrayMode(Array::Contiguous, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(ArrayWithContiguous): return ArrayMode(Array::Contiguous, Array::Array, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous): return ArrayMode(Array::Contiguous, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe); + case asArrayModes(NonArrayWithArrayStorage): return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(NonArrayWithSlowPutArrayStorage): @@ -62,36 +90,39 @@ ArrayMode ArrayMode::fromObserved(ArrayProfile* profile, Array::Action action, b case asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage): case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage): return ArrayMode(Array::SlowPutArrayStorage, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe); - case asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage): - return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::Convert).withProfile(profile, makeSafe); - case asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage): - return ArrayMode(Array::ArrayStorage, Array::Array, Array::Convert).withProfile(profile, makeSafe); - case asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage): - return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::Convert).withProfile(profile, makeSafe); - case asArrayModes(NonArray) | asArrayModes(NonArrayWithContiguous): - if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) - 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 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 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 ArrayMode(Array::SelectUsingPredictions); + if ((observed & asArrayModes(NonArray)) && profile->mayInterceptIndexedAccesses()) + return ArrayMode(Array::SelectUsingPredictions); + + Array::Type type; + Array::Class arrayClass; + + if (shouldUseSlowPutArrayStorage(observed)) + type = Array::SlowPutArrayStorage; + else if (shouldUseFastArrayStorage(observed)) + type = Array::ArrayStorage; + else if (shouldUseContiguous(observed)) + type = Array::Contiguous; + else if (shouldUseDouble(observed)) + type = Array::Double; + else if (shouldUseInt32(observed)) + type = Array::Int32; + else + type = Array::Undecided; + + if (observed & (asArrayModes(ArrayWithUndecided) | asArrayModes(ArrayWithInt32) | asArrayModes(ArrayWithDouble) | asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage))) + arrayClass = Array::Array; + else if (observed & (asArrayModes(NonArray) | asArrayModes(NonArrayWithInt32) | asArrayModes(NonArrayWithDouble) | asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage))) + arrayClass = Array::NonArray; + else + arrayClass = Array::PossiblyArray; + + return ArrayMode(type, arrayClass, Array::Convert).withProfile(profile, makeSafe); } } -ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index) const +ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index, SpeculatedType value) const { if (!base || !index) { // It can be that we had a legitimate arrayMode but no incoming predictions. That'll @@ -104,52 +135,124 @@ ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index) const if (!isInt32Speculation(index) || !isCellSpeculation(base)) return ArrayMode(Array::Generic); - 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. + switch (type()) { + case Array::Unprofiled: return ArrayMode(Array::ForceExit); - } - - if (type() != Array::SelectUsingPredictions) + + case Array::Undecided: + if (!value) + return withType(Array::ForceExit); + if (isInt32Speculation(value)) + return withTypeAndConversion(Array::Int32, Array::Convert); + if (isNumberSpeculation(value)) + return withTypeAndConversion(Array::Double, Array::Convert); + return withTypeAndConversion(Array::Contiguous, Array::Convert); + + case Array::Int32: + if (!value || isInt32Speculation(value)) + return *this; + if (isNumberSpeculation(value)) + return withTypeAndConversion(Array::Double, Array::Convert); + return withTypeAndConversion(Array::Contiguous, Array::Convert); + + case Array::Double: + if (!value || isNumberSpeculation(value)) + return *this; + return withTypeAndConversion(Array::Contiguous, Array::Convert); + + case Array::SelectUsingPredictions: + if (isStringSpeculation(base)) + return ArrayMode(Array::String); + + if (isArgumentsSpeculation(base)) + return ArrayMode(Array::Arguments); + + if (isInt8ArraySpeculation(base)) + return ArrayMode(Array::Int8Array); + + if (isInt16ArraySpeculation(base)) + return ArrayMode(Array::Int16Array); + + if (isInt32ArraySpeculation(base)) + return ArrayMode(Array::Int32Array); + + if (isUint8ArraySpeculation(base)) + return ArrayMode(Array::Uint8Array); + + if (isUint8ClampedArraySpeculation(base)) + return ArrayMode(Array::Uint8ClampedArray); + + if (isUint16ArraySpeculation(base)) + return ArrayMode(Array::Uint16Array); + + if (isUint32ArraySpeculation(base)) + return ArrayMode(Array::Uint32Array); + + if (isFloat32ArraySpeculation(base)) + return ArrayMode(Array::Float32Array); + + if (isFloat64ArraySpeculation(base)) + return ArrayMode(Array::Float64Array); + + return ArrayMode(Array::Generic); + + default: return *this; + } +} + +Structure* ArrayMode::originalArrayStructure(Graph& graph, const CodeOrigin& codeOrigin) const +{ + if (!isJSArrayWithOriginalStructure()) + return 0; - if (isStringSpeculation(base)) - return ArrayMode(Array::String); - - if (isArgumentsSpeculation(base)) - return ArrayMode(Array::Arguments); - - if (isInt8ArraySpeculation(base)) - return ArrayMode(Array::Int8Array); - - if (isInt16ArraySpeculation(base)) - return ArrayMode(Array::Int16Array); - - if (isInt32ArraySpeculation(base)) - return ArrayMode(Array::Int32Array); - - if (isUint8ArraySpeculation(base)) - return ArrayMode(Array::Uint8Array); - - if (isUint8ClampedArraySpeculation(base)) - return ArrayMode(Array::Uint8ClampedArray); - - if (isUint16ArraySpeculation(base)) - return ArrayMode(Array::Uint16Array); - - if (isUint32ArraySpeculation(base)) - return ArrayMode(Array::Uint32Array); - - if (isFloat32ArraySpeculation(base)) - return ArrayMode(Array::Float32Array); - - if (isFloat64ArraySpeculation(base)) - return ArrayMode(Array::Float64Array); + JSGlobalObject* globalObject = graph.globalObjectFor(codeOrigin); - return ArrayMode(Array::Generic); + switch (type()) { + case Array::Int32: + return globalObject->originalArrayStructureForIndexingType(ArrayWithInt32); + case Array::Double: + return globalObject->originalArrayStructureForIndexingType(ArrayWithDouble); + case Array::Contiguous: + return globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous); + case Array::ArrayStorage: + return globalObject->originalArrayStructureForIndexingType(ArrayWithArrayStorage); + default: + CRASH(); + return 0; + } +} + +Structure* ArrayMode::originalArrayStructure(Graph& graph, Node& node) const +{ + return originalArrayStructure(graph, node.codeOrigin); +} + +bool ArrayMode::alreadyChecked(Graph& graph, Node& node, AbstractValue& value, IndexingType shape) const +{ + switch (arrayClass()) { + case Array::OriginalArray: + return value.m_currentKnownStructure.hasSingleton() + && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape + && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray) + && graph.globalObjectFor(node.codeOrigin)->isOriginalArrayStructure(value.m_currentKnownStructure.singleton()); + + case Array::Array: + if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(shape | IsArray))) + return true; + return value.m_currentKnownStructure.hasSingleton() + && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape + && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); + + default: + if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(shape) | asArrayModes(shape | IsArray))) + return true; + return value.m_currentKnownStructure.hasSingleton() + && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape; + } } -bool ArrayMode::alreadyChecked(AbstractValue& value) const +bool ArrayMode::alreadyChecked(Graph& graph, Node& node, AbstractValue& value) const { switch (type()) { case Array::Generic: @@ -161,44 +264,37 @@ bool ArrayMode::alreadyChecked(AbstractValue& value) const case Array::String: return speculationChecked(value.m_type, SpecString); + case Array::Int32: + return alreadyChecked(graph, node, value, Int32Shape); + + case Array::Double: + return alreadyChecked(graph, node, value, DoubleShape); + case Array::Contiguous: - 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()); + return alreadyChecked(graph, node, value, ContiguousShape); case Array::ArrayStorage: - 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()); + return alreadyChecked(graph, node, value, ArrayStorageShape); case Array::SlowPutArrayStorage: - if (isJSArray()) { + switch (arrayClass()) { + case Array::OriginalArray: + CRASH(); + return false; + + case Array::Array: 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); + + default: + 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()); } - 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::Arguments: return speculationChecked(value.m_type, SpecArguments); @@ -232,6 +328,7 @@ bool ArrayMode::alreadyChecked(AbstractValue& value) const case Array::SelectUsingPredictions: case Array::Unprofiled: + case Array::Undecided: break; } @@ -252,6 +349,12 @@ const char* arrayTypeToString(Array::Type type) return "ForceExit"; case Array::String: return "String"; + case Array::Undecided: + return "Undecided"; + case Array::Int32: + return "Int32"; + case Array::Double: + return "Double"; case Array::Contiguous: return "Contiguous"; case Array::ArrayStorage: @@ -306,6 +409,8 @@ const char* arrayClassToString(Array::Class arrayClass) const char* arraySpeculationToString(Array::Speculation speculation) { switch (speculation) { + case Array::SaneChain: + return "SaneChain"; case Array::InBounds: return "InBounds"; case Array::ToHole: diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.h b/Source/JavaScriptCore/dfg/DFGArrayMode.h index 615965c92..0799868d6 100644 --- a/Source/JavaScriptCore/dfg/DFGArrayMode.h +++ b/Source/JavaScriptCore/dfg/DFGArrayMode.h @@ -33,9 +33,15 @@ #include "ArrayProfile.h" #include "SpeculatedType.h" -namespace JSC { namespace DFG { +namespace JSC { +struct CodeOrigin; + +namespace DFG { + +class Graph; struct AbstractValue; +struct Node; // Use a namespace + enum instead of enum alone to avoid the namespace collision // that would otherwise occur, since we say things like "Int8Array" and "JSArray" @@ -52,7 +58,10 @@ enum Type { ForceExit, // Implies that we have no idea how to execute this operation, so we should just give up. Generic, String, - + + Undecided, + Int32, + Double, Contiguous, ArrayStorage, SlowPutArrayStorage, @@ -77,11 +86,11 @@ enum Class { }; enum Speculation { - InBounds, - ToHole, - OutOfBounds + SaneChain, // In bounds and the array prototype chain is still intact, i.e. loading a hole doesn't require special treatment. + InBounds, // In bounds and not loading a hole. + ToHole, // Potentially storing to a hole. + OutOfBounds // Out-of-bounds access and anything can happen. }; - enum Conversion { AsIs, Convert @@ -159,7 +168,7 @@ public: mySpeculation = Array::InBounds; if (isJSArray()) { - if (profile->usesOriginalArrayStructures()) + if (profile->usesOriginalArrayStructures() && benefitsFromOriginalArray()) myArrayClass = Array::OriginalArray; else myArrayClass = Array::Array; @@ -169,15 +178,27 @@ public: return ArrayMode(type(), myArrayClass, mySpeculation, conversion()); } - ArrayMode refine(SpeculatedType base, SpeculatedType index) const; + ArrayMode withType(Array::Type type) const + { + return ArrayMode(type, arrayClass(), speculation(), conversion()); + } - bool alreadyChecked(AbstractValue&) const; + ArrayMode withTypeAndConversion(Array::Type type, Array::Conversion conversion) const + { + return ArrayMode(type, arrayClass(), speculation(), conversion); + } + + ArrayMode refine(SpeculatedType base, SpeculatedType index, SpeculatedType value = SpecNone) const; + + bool alreadyChecked(Graph&, Node&, AbstractValue&) const; const char* toString() const; bool usesButterfly() const { switch (type()) { + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: case Array::SlowPutArrayStorage: @@ -203,9 +224,20 @@ public: return arrayClass() == Array::OriginalArray; } + bool isSaneChain() const + { + return speculation() == Array::SaneChain; + } + bool isInBounds() const { - return speculation() == Array::InBounds; + switch (speculation()) { + case Array::SaneChain: + case Array::InBounds: + return true; + default: + return false; + } } bool mayStoreToHole() const @@ -263,6 +295,7 @@ public: case Array::Unprofiled: case Array::ForceExit: case Array::Generic: + case Array::Undecided: return false; default: return true; @@ -277,6 +310,8 @@ public: case Array::ForceExit: case Array::Generic: return false; + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: case Array::SlowPutArrayStorage: @@ -286,6 +321,23 @@ public: } } + bool benefitsFromOriginalArray() const + { + switch (type()) { + case Array::Int32: + case Array::Double: + case Array::Contiguous: + case Array::ArrayStorage: + return true; + default: + return false; + } + } + + // Returns 0 if this is not OriginalArray. + Structure* originalArrayStructure(Graph&, const CodeOrigin&) const; + Structure* originalArrayStructure(Graph&, Node&) const; + bool benefitsFromStructureCheck() const { switch (type()) { @@ -309,6 +361,10 @@ public: switch (type()) { case Array::Generic: return ALL_ARRAY_MODES; + case Array::Int32: + return arrayModesWithIndexingShape(Int32Shape); + case Array::Double: + return arrayModesWithIndexingShape(DoubleShape); case Array::Contiguous: return arrayModesWithIndexingShape(ContiguousShape); case Array::ArrayStorage: @@ -354,6 +410,8 @@ private: } } + bool alreadyChecked(Graph&, Node&, AbstractValue&, IndexingType shape) const; + union { struct { uint8_t type; diff --git a/Source/JavaScriptCore/dfg/DFGBasicBlock.h b/Source/JavaScriptCore/dfg/DFGBasicBlock.h index 441e2e75e..6f348f2e1 100644 --- a/Source/JavaScriptCore/dfg/DFGBasicBlock.h +++ b/Source/JavaScriptCore/dfg/DFGBasicBlock.h @@ -29,6 +29,7 @@ #if ENABLE(DFG_JIT) #include "DFGAbstractValue.h" +#include "DFGBranchDirection.h" #include "DFGNode.h" #include "Operands.h" #include <wtf/OwnPtr.h> @@ -46,6 +47,7 @@ struct BasicBlock : Vector<NodeIndex, 8> { , cfaShouldRevisit(false) , cfaFoundConstants(false) , cfaDidFinish(true) + , cfaBranchDirection(InvalidBranchDirection) #if !ASSERT_DISABLED , isLinked(false) #endif @@ -105,6 +107,7 @@ struct BasicBlock : Vector<NodeIndex, 8> { bool cfaShouldRevisit; bool cfaFoundConstants; bool cfaDidFinish; + BranchDirection cfaBranchDirection; #if !ASSERT_DISABLED bool isLinked; #endif diff --git a/Source/JavaScriptCore/dfg/DFGBranchDirection.h b/Source/JavaScriptCore/dfg/DFGBranchDirection.h new file mode 100644 index 000000000..8bbe3c635 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGBranchDirection.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DFGBranchDirection_h +#define DFGBranchDirection_h + +#include <wtf/Platform.h> + +#if ENABLE(DFG_JIT) + +namespace JSC { namespace DFG { + +enum BranchDirection { + // This is not a branch and so there is no branch direction, or + // the branch direction has yet to be set. + InvalidBranchDirection, + + // The branch takes the true case. + TakeTrue, + + // The branch takes the false case. + TakeFalse, + + // For all we know, the branch could go either direction, so we + // have to assume the worst. + TakeBoth +}; + +static inline const char* branchDirectionToString(BranchDirection branchDirection) +{ + switch (branchDirection) { + case InvalidBranchDirection: + return "Invalid"; + case TakeTrue: + return "TakeTrue"; + case TakeFalse: + return "TakeFalse"; + case TakeBoth: + return "TakeBoth"; + } +} + +static inline bool isKnownDirection(BranchDirection branchDirection) +{ + switch (branchDirection) { + case TakeTrue: + case TakeFalse: + return true; + default: + return false; + } +} + +static inline bool branchCondition(BranchDirection branchDirection) +{ + if (branchDirection == TakeTrue) + return true; + ASSERT(branchDirection == TakeFalse); + return false; +} + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + +#endif // DFGBranchDirection_h diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index 70aa2b637..9b879b9e3 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -218,7 +218,7 @@ private: if (operand == JSStack::Callee) return getCallee(); - + // Is this an argument? if (operandIsArgument(operand)) return getArgument(operand); @@ -256,7 +256,7 @@ private: m_inlineStackTop->m_lazyOperands.prediction( LazyOperandValueProfileKey(m_currentIndex, node.local())); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Lazy operand [@%u, bc#%u, r%d] prediction: %s\n", + dataLogF("Lazy operand [@%u, bc#%u, r%d] prediction: %s\n", nodeIndex, m_currentIndex, node.local(), speculationToString(prediction)); #endif node.variableAccessData()->predict(prediction); @@ -876,7 +876,7 @@ private: SpeculatedType prediction = m_inlineStackTop->m_profiledBlock->valueProfilePredictionForBytecodeOffset(bytecodeIndex); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Dynamic [@%u, bc#%u] prediction: %s\n", nodeIndex, bytecodeIndex, speculationToString(prediction)); + dataLogF("Dynamic [@%u, bc#%u] prediction: %s\n", nodeIndex, bytecodeIndex, speculationToString(prediction)); #endif return prediction; @@ -905,10 +905,15 @@ private: return getPrediction(m_graph.size(), m_currentProfilingIndex); } - ArrayMode getArrayMode(ArrayProfile* profile) + ArrayMode getArrayMode(ArrayProfile* profile, Array::Action action) { profile->computeUpdatedPrediction(m_inlineStackTop->m_codeBlock); - return ArrayMode::fromObserved(profile, Array::Read, false); + return ArrayMode::fromObserved(profile, action, false); + } + + ArrayMode getArrayMode(ArrayProfile* profile) + { + return getArrayMode(profile, Array::Read); } ArrayMode getArrayModeAndEmitChecks(ArrayProfile* profile, Array::Action action, NodeIndex base) @@ -917,8 +922,8 @@ private: #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%s, %u\n", m_currentIndex, profile->expectedStructure(), profile->structureIsPolymorphic() ? " (polymorphic)" : "", profile->mayInterceptIndexedAccesses() ? " (may intercept)" : "", profile->observedArrayModes()); + dataLogF("Slow case profile for bc#%u: %u\n", m_currentIndex, m_inlineStackTop->m_profiledBlock->rareCaseProfileForBytecodeOffset(m_currentIndex)->m_counter); + dataLogF("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 = @@ -962,13 +967,13 @@ private: if (m_inlineStackTop->m_profiledBlock->likelyToTakeDeepestSlowCase(m_currentIndex) || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow)) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Making ArithMul @%u take deepest slow case.\n", nodeIndex); + dataLogF("Making ArithMul @%u take deepest slow case.\n", nodeIndex); #endif m_graph[nodeIndex].mergeFlags(NodeMayOverflow | NodeMayNegZero); } else if (m_inlineStackTop->m_profiledBlock->likelyToTakeSlowCase(m_currentIndex) || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Making ArithMul @%u take faster slow case.\n", nodeIndex); + dataLogF("Making ArithMul @%u take faster slow case.\n", nodeIndex); #endif m_graph[nodeIndex].mergeFlags(NodeMayNegZero); } @@ -998,7 +1003,7 @@ private: return nodeIndex; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Making %s @%u safe at bc#%u because special fast-case counter is at %u and exit profiles say %d, %d\n", Graph::opName(m_graph[nodeIndex].op()), nodeIndex, m_currentIndex, m_inlineStackTop->m_profiledBlock->specialFastCaseProfileForBytecodeOffset(m_currentIndex)->m_counter, m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow), m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)); + dataLogF("Making %s @%u safe at bc#%u because special fast-case counter is at %u and exit profiles say %d, %d\n", Graph::opName(m_graph[nodeIndex].op()), nodeIndex, m_currentIndex, m_inlineStackTop->m_profiledBlock->specialFastCaseProfileForBytecodeOffset(m_currentIndex)->m_counter, m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow), m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)); #endif // FIXME: It might be possible to make this more granular. The DFG certainly can @@ -1272,19 +1277,19 @@ void ByteCodeParser::handleCall(Interpreter* interpreter, Instruction* currentIn m_inlineStackTop->m_profiledBlock, m_currentIndex); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("For call at @%lu bc#%u: ", m_graph.size(), m_currentIndex); + dataLogF("For call at @%lu bc#%u: ", m_graph.size(), m_currentIndex); if (callLinkStatus.isSet()) { if (callLinkStatus.couldTakeSlowPath()) - dataLog("could take slow path, "); - dataLog("target = %p\n", callLinkStatus.callTarget()); + dataLogF("could take slow path, "); + dataLogF("target = %p\n", callLinkStatus.callTarget()); } else - dataLog("not set.\n"); + dataLogF("not set.\n"); #endif if (m_graph.isFunctionConstant(callTarget)) { callType = ConstantFunction; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Call at [@%lu, bc#%u] has a function constant: %p, exec %p.\n", + dataLogF("Call at [@%lu, bc#%u] has a function constant: %p, exec %p.\n", m_graph.size(), m_currentIndex, m_graph.valueOfFunctionConstant(callTarget), m_graph.valueOfFunctionConstant(callTarget)->executable()); @@ -1292,7 +1297,7 @@ void ByteCodeParser::handleCall(Interpreter* interpreter, Instruction* currentIn } else if (m_graph.isInternalFunctionConstant(callTarget)) { callType = ConstantInternalFunction; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Call at [@%lu, bc#%u] has an internal function constant: %p.\n", + dataLogF("Call at [@%lu, bc#%u] has an internal function constant: %p.\n", m_graph.size(), m_currentIndex, m_graph.valueOfInternalFunctionConstant(callTarget)); #endif @@ -1300,14 +1305,14 @@ void ByteCodeParser::handleCall(Interpreter* interpreter, Instruction* currentIn && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCache)) { callType = LinkedFunction; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Call at [@%lu, bc#%u] is linked to: %p, exec %p.\n", + dataLogF("Call at [@%lu, bc#%u] is linked to: %p, exec %p.\n", m_graph.size(), m_currentIndex, callLinkStatus.callTarget(), callLinkStatus.callTarget()->executable()); #endif } else { callType = UnknownFunction; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Call at [@%lu, bc#%u] is has an unknown or ambiguous target.\n", + dataLogF("Call at [@%lu, bc#%u] is has an unknown or ambiguous target.\n", m_graph.size(), m_currentIndex); #endif } @@ -1432,7 +1437,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c ASSERT(canInlineFunctionFor(codeBlock, kind)); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Inlining executable %p.\n", executable); + dataLogF("Inlining executable %p.\n", executable); #endif // Now we know without a doubt that we are committed to inlining. So begin the process @@ -1517,7 +1522,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c // caller. It doesn't need to be linked to, but it needs outgoing links. if (!inlineStackEntry.m_unlinkedBlocks.isEmpty()) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Reascribing bytecode index of block %p from bc#%u to bc#%u (inline return case).\n", lastBlock, lastBlock->bytecodeBegin, m_currentIndex); + dataLogF("Reascribing bytecode index of block %p from bc#%u to bc#%u (inline return case).\n", lastBlock, lastBlock->bytecodeBegin, m_currentIndex); #endif // For debugging purposes, set the bytecodeBegin. Note that this doesn't matter // for release builds because this block will never serve as a potential target @@ -1529,7 +1534,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c m_currentBlock = m_graph.m_blocks.last().get(); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Done inlining executable %p, continuing code generation at epilogue.\n", executable); + dataLogF("Done inlining executable %p, continuing code generation at epilogue.\n", executable); #endif return true; } @@ -1556,7 +1561,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c // Need to create a new basic block for the continuation at the caller. OwnPtr<BasicBlock> block = adoptPtr(new BasicBlock(nextOffset, m_numArguments, m_numLocals)); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Creating inline epilogue basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); + dataLogF("Creating inline epilogue basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); #endif m_currentBlock = block.get(); ASSERT(m_inlineStackTop->m_caller->m_blockLinkingTargets.isEmpty() || m_graph.m_blocks[m_inlineStackTop->m_caller->m_blockLinkingTargets.last()]->bytecodeBegin < nextOffset); @@ -1568,7 +1573,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c // At this point we return and continue to generate code for the caller, but // in the new basic block. #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Done inlining executable %p, continuing code generation in new block.\n", executable); + dataLogF("Done inlining executable %p, continuing code generation in new block.\n", executable); #endif return true; } @@ -1649,7 +1654,12 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins return false; ArrayMode arrayMode = getArrayMode(m_currentInstruction[5].u.arrayProfile); + if (!arrayMode.isJSArray()) + return false; switch (arrayMode.type()) { + case Array::Undecided: + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: { NodeIndex arrayPush = addToGraph(ArrayPush, OpInfo(arrayMode.asWord()), OpInfo(prediction), get(registerOffset + argumentToOperand(0)), get(registerOffset + argumentToOperand(1))); @@ -1669,7 +1679,11 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins return false; ArrayMode arrayMode = getArrayMode(m_currentInstruction[5].u.arrayProfile); + if (!arrayMode.isJSArray()) + return false; switch (arrayMode.type()) { + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: { NodeIndex arrayPop = addToGraph(ArrayPop, OpInfo(arrayMode.asWord()), OpInfo(prediction), get(registerOffset + argumentToOperand(0))); @@ -1689,7 +1703,7 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins int thisOperand = registerOffset + argumentToOperand(0); int indexOperand = registerOffset + argumentToOperand(1); - NodeIndex charCode = addToGraph(StringCharCodeAt, OpInfo(Array::String), get(thisOperand), getToInt32(indexOperand)); + NodeIndex charCode = addToGraph(StringCharCodeAt, OpInfo(ArrayMode(Array::String).asWord()), get(thisOperand), getToInt32(indexOperand)); if (usesResult) set(resultOperand, charCode); @@ -1702,7 +1716,7 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins int thisOperand = registerOffset + argumentToOperand(0); int indexOperand = registerOffset + argumentToOperand(1); - NodeIndex charCode = addToGraph(StringCharAt, OpInfo(Array::String), get(thisOperand), getToInt32(indexOperand)); + NodeIndex charCode = addToGraph(StringCharAt, OpInfo(ArrayMode(Array::String).asWord()), get(thisOperand), getToInt32(indexOperand)); if (usesResult) set(resultOperand, charCode); @@ -1754,7 +1768,7 @@ bool ByteCodeParser::handleConstantInternalFunction( if (argumentCountIncludingThis == 2) { setIntrinsicResult( usesResult, resultOperand, - addToGraph(NewArrayWithSize, get(registerOffset + argumentToOperand(1)))); + addToGraph(NewArrayWithSize, OpInfo(ArrayWithUndecided), get(registerOffset + argumentToOperand(1)))); return true; } @@ -1762,7 +1776,7 @@ bool ByteCodeParser::handleConstantInternalFunction( addVarArgChild(get(registerOffset + argumentToOperand(i))); setIntrinsicResult( usesResult, resultOperand, - addToGraph(Node::VarArg, NewArray, OpInfo(0), OpInfo(0))); + addToGraph(Node::VarArg, NewArray, OpInfo(ArrayWithUndecided), OpInfo(0))); return true; } @@ -2063,7 +2077,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) addToGraph(Jump, OpInfo(m_currentIndex)); else { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Refusing to plant jump at limit %u because block %p is empty.\n", limit, m_currentBlock); + dataLogF("Refusing to plant jump at limit %u because block %p is empty.\n", limit, m_currentBlock); #endif } return shouldContinueParsing; @@ -2090,9 +2104,9 @@ bool ByteCodeParser::parseBlock(unsigned limit) m_inlineStackTop->m_profiledBlock->valueProfileForBytecodeOffset(m_currentProfilingIndex); profile->computeUpdatedPrediction(); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("[@%lu bc#%u]: profile %p: ", m_graph.size(), m_currentProfilingIndex, profile); + dataLogF("[@%lu bc#%u]: profile %p: ", m_graph.size(), m_currentProfilingIndex, profile); profile->dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif if (profile->m_singletonValueIsTop || !profile->m_singletonValue @@ -2110,36 +2124,65 @@ bool ByteCodeParser::parseBlock(unsigned limit) } case op_create_this: { - set(currentInstruction[1].u.operand, addToGraph(CreateThis, get(JSStack::Callee))); + int calleeOperand = currentInstruction[2].u.operand; + NodeIndex callee = get(calleeOperand); + bool alreadyEmitted = false; + if (m_graph[callee].op() == WeakJSConstant) { + JSCell* cell = m_graph[callee].weakConstant(); + ASSERT(cell->inherits(&JSFunction::s_info)); + + JSFunction* function = jsCast<JSFunction*>(cell); + Structure* inheritorID = function->tryGetKnownInheritorID(); + if (inheritorID) { + addToGraph(InheritorIDWatchpoint, OpInfo(function)); + set(currentInstruction[1].u.operand, addToGraph(NewObject, OpInfo(inheritorID))); + alreadyEmitted = true; + } + } + if (!alreadyEmitted) + set(currentInstruction[1].u.operand, addToGraph(CreateThis, callee)); NEXT_OPCODE(op_create_this); } case op_new_object: { - set(currentInstruction[1].u.operand, addToGraph(NewObject)); + set(currentInstruction[1].u.operand, addToGraph(NewObject, OpInfo(m_inlineStackTop->m_codeBlock->globalObject()->emptyObjectStructure()))); NEXT_OPCODE(op_new_object); } case op_new_array: { int startOperand = currentInstruction[2].u.operand; int numOperands = currentInstruction[3].u.operand; + ArrayAllocationProfile* profile = currentInstruction[4].u.arrayAllocationProfile; for (int operandIdx = startOperand; operandIdx < startOperand + numOperands; ++operandIdx) addVarArgChild(get(operandIdx)); - set(currentInstruction[1].u.operand, addToGraph(Node::VarArg, NewArray, OpInfo(0), OpInfo(0))); + set(currentInstruction[1].u.operand, addToGraph(Node::VarArg, NewArray, OpInfo(profile->selectIndexingType()), OpInfo(0))); NEXT_OPCODE(op_new_array); } case op_new_array_with_size: { int lengthOperand = currentInstruction[2].u.operand; - set(currentInstruction[1].u.operand, addToGraph(NewArrayWithSize, get(lengthOperand))); + ArrayAllocationProfile* profile = currentInstruction[3].u.arrayAllocationProfile; + set(currentInstruction[1].u.operand, addToGraph(NewArrayWithSize, OpInfo(profile->selectIndexingType()), get(lengthOperand))); NEXT_OPCODE(op_new_array_with_size); } case op_new_array_buffer: { int startConstant = currentInstruction[2].u.operand; int numConstants = currentInstruction[3].u.operand; + ArrayAllocationProfile* profile = currentInstruction[4].u.arrayAllocationProfile; NewArrayBufferData data; data.startConstant = m_inlineStackTop->m_constantBufferRemap[startConstant]; data.numConstants = numConstants; + data.indexingType = profile->selectIndexingType(); + + // If this statement has never executed, we'll have the wrong indexing type in the profile. + for (int i = 0; i < numConstants; ++i) { + data.indexingType = + leastUpperBoundOfIndexingTypeAndValue( + data.indexingType, + m_codeBlock->constantBuffer(data.startConstant)[i]); + } + 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); @@ -2150,6 +2193,22 @@ bool ByteCodeParser::parseBlock(unsigned limit) NEXT_OPCODE(op_new_regexp); } + case op_get_callee: { + ValueProfile* profile = currentInstruction[2].u.profile; + profile->computeUpdatedPrediction(); + if (profile->m_singletonValueIsTop + || !profile->m_singletonValue + || !profile->m_singletonValue.isCell()) + set(currentInstruction[1].u.operand, get(JSStack::Callee)); + else { + ASSERT(profile->m_singletonValue.asCell()->inherits(&JSFunction::s_info)); + NodeIndex actualCallee = get(JSStack::Callee); + addToGraph(CheckFunction, OpInfo(profile->m_singletonValue.asCell()), actualCallee); + set(currentInstruction[1].u.operand, addToGraph(WeakJSConstant, OpInfo(profile->m_singletonValue.asCell()))); + } + NEXT_OPCODE(op_get_callee); + } + // === Bitwise operations === case op_bitand: { @@ -3215,12 +3274,12 @@ void ByteCodeParser::processPhiStack() VariableAccessData* dataForPhi = m_graph[entry.m_phi].variableAccessData(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Handling phi entry for var %u, phi @%u.\n", entry.m_varNo, entry.m_phi); + dataLogF(" Handling phi entry for var %u, phi @%u.\n", entry.m_varNo, entry.m_phi); #endif for (size_t i = 0; i < predecessors.size(); ++i) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Dealing with predecessor block %u.\n", predecessors[i]); + dataLogF(" Dealing with predecessor block %u.\n", predecessors[i]); #endif BasicBlock* predecessorBlock = m_graph.m_blocks[predecessors[i]].get(); @@ -3230,7 +3289,7 @@ void ByteCodeParser::processPhiStack() NodeIndex valueInPredecessor = var; if (valueInPredecessor == NoNode) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Did not find node, adding phi.\n"); + dataLogF(" Did not find node, adding phi.\n"); #endif valueInPredecessor = insertPhiNode(OpInfo(newVariableAccessData(stackType == ArgumentPhiStack ? argumentToOperand(varNo) : static_cast<int>(varNo), false)), predecessorBlock); @@ -3242,7 +3301,7 @@ void ByteCodeParser::processPhiStack() phiStack.append(PhiStackEntry(predecessorBlock, valueInPredecessor, varNo)); } else if (m_graph[valueInPredecessor].op() == GetLocal) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Found GetLocal @%u.\n", valueInPredecessor); + dataLogF(" Found GetLocal @%u.\n", valueInPredecessor); #endif // We want to ensure that the VariableAccessDatas are identical between the @@ -3254,7 +3313,7 @@ void ByteCodeParser::processPhiStack() valueInPredecessor = m_graph[valueInPredecessor].child1().index(); } else { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Found @%u.\n", valueInPredecessor); + dataLogF(" Found @%u.\n", valueInPredecessor); #endif } ASSERT(m_graph[valueInPredecessor].op() == SetLocal @@ -3269,48 +3328,48 @@ void ByteCodeParser::processPhiStack() Node* phiNode = &m_graph[entry.m_phi]; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Ref count of @%u = %u.\n", entry.m_phi, phiNode->refCount()); + dataLogF(" Ref count of @%u = %u.\n", entry.m_phi, phiNode->refCount()); #endif if (phiNode->refCount()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Reffing @%u.\n", valueInPredecessor); + dataLogF(" Reffing @%u.\n", valueInPredecessor); #endif m_graph.ref(valueInPredecessor); } if (!phiNode->child1()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Setting @%u->child1 = @%u.\n", entry.m_phi, valueInPredecessor); + dataLogF(" Setting @%u->child1 = @%u.\n", entry.m_phi, valueInPredecessor); #endif phiNode->children.setChild1(Edge(valueInPredecessor)); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Children of @%u: ", entry.m_phi); + dataLogF(" Children of @%u: ", entry.m_phi); phiNode->dumpChildren(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif continue; } if (!phiNode->child2()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Setting @%u->child2 = @%u.\n", entry.m_phi, valueInPredecessor); + dataLogF(" Setting @%u->child2 = @%u.\n", entry.m_phi, valueInPredecessor); #endif phiNode->children.setChild2(Edge(valueInPredecessor)); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Children of @%u: ", entry.m_phi); + dataLogF(" Children of @%u: ", entry.m_phi); phiNode->dumpChildren(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif continue; } if (!phiNode->child3()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Setting @%u->child3 = @%u.\n", entry.m_phi, valueInPredecessor); + dataLogF(" Setting @%u->child3 = @%u.\n", entry.m_phi, valueInPredecessor); #endif phiNode->children.setChild3(Edge(valueInPredecessor)); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Children of @%u: ", entry.m_phi); + dataLogF(" Children of @%u: ", entry.m_phi); phiNode->dumpChildren(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif continue; } @@ -3318,7 +3377,7 @@ void ByteCodeParser::processPhiStack() NodeIndex newPhi = insertPhiNode(OpInfo(dataForPhi), entry.m_block); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Splitting @%u, created @%u.\n", entry.m_phi, newPhi); + dataLogF(" Splitting @%u, created @%u.\n", entry.m_phi, newPhi); #endif phiNode = &m_graph[entry.m_phi]; // reload after vector resize @@ -3329,17 +3388,17 @@ void ByteCodeParser::processPhiStack() newPhiNode.children = phiNode->children; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Children of @%u: ", newPhi); + dataLogF(" Children of @%u: ", newPhi); newPhiNode.dumpChildren(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif phiNode->children.initialize(newPhi, valueInPredecessor, NoNode); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Children of @%u: ", entry.m_phi); + dataLogF(" Children of @%u: ", entry.m_phi); phiNode->dumpChildren(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif } } @@ -3366,7 +3425,7 @@ void ByteCodeParser::linkBlock(BasicBlock* block, Vector<BlockIndex>& possibleTa case Jump: node.setTakenBlockIndex(m_graph.blockIndexForBytecodeOffset(possibleTargets, node.takenBytecodeOffsetDuringParsing())); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Linked basic block %p to %p, #%u.\n", block, m_graph.m_blocks[node.takenBlockIndex()].get(), node.takenBlockIndex()); + dataLogF("Linked basic block %p to %p, #%u.\n", block, m_graph.m_blocks[node.takenBlockIndex()].get(), node.takenBlockIndex()); #endif break; @@ -3374,13 +3433,13 @@ void ByteCodeParser::linkBlock(BasicBlock* block, Vector<BlockIndex>& possibleTa node.setTakenBlockIndex(m_graph.blockIndexForBytecodeOffset(possibleTargets, node.takenBytecodeOffsetDuringParsing())); node.setNotTakenBlockIndex(m_graph.blockIndexForBytecodeOffset(possibleTargets, node.notTakenBytecodeOffsetDuringParsing())); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Linked basic block %p to %p, #%u and %p, #%u.\n", block, m_graph.m_blocks[node.takenBlockIndex()].get(), node.takenBlockIndex(), m_graph.m_blocks[node.notTakenBlockIndex()].get(), node.notTakenBlockIndex()); + dataLogF("Linked basic block %p to %p, #%u and %p, #%u.\n", block, m_graph.m_blocks[node.takenBlockIndex()].get(), node.takenBlockIndex(), m_graph.m_blocks[node.notTakenBlockIndex()].get(), node.notTakenBlockIndex()); #endif break; default: #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Marking basic block %p as linked.\n", block); + dataLogF("Marking basic block %p as linked.\n", block); #endif break; } @@ -3489,9 +3548,9 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry( } #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Current captured variables: "); + dataLogF("Current captured variables: "); inlineCallFrame.capturedVars.dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif byteCodeParser->m_codeBlock->inlineCallFrames().append(inlineCallFrame); @@ -3598,7 +3657,7 @@ void ByteCodeParser::parseCodeBlock() CodeBlock* codeBlock = m_inlineStackTop->m_codeBlock; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Parsing code block %p. codeType = %s, captureCount = %u, needsFullScopeChain = %s, needsActivation = %s, isStrictMode = %s\n", + dataLogF("Parsing code block %p. codeType = %s, captureCount = %u, needsFullScopeChain = %s, needsActivation = %s, isStrictMode = %s\n", codeBlock, codeTypeToString(codeBlock->codeType()), codeBlock->symbolTable() ? codeBlock->symbolTable()->captureCount() : 0, @@ -3612,7 +3671,7 @@ void ByteCodeParser::parseCodeBlock() // The maximum bytecode offset to go into the current basicblock is either the next jump target, or the end of the instructions. unsigned limit = jumpTargetIndex < codeBlock->numberOfJumpTargets() ? codeBlock->jumpTarget(jumpTargetIndex) : codeBlock->instructions().size(); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Parsing bytecode with limit %p bc#%u at inline depth %u.\n", m_inlineStackTop->executable(), limit, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); + dataLogF("Parsing bytecode with limit %p bc#%u at inline depth %u.\n", m_inlineStackTop->executable(), limit, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); #endif ASSERT(m_currentIndex < limit); @@ -3634,13 +3693,13 @@ void ByteCodeParser::parseCodeBlock() // Change its bytecode begin and continue. m_currentBlock = m_graph.m_blocks.last().get(); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Reascribing bytecode index of block %p from bc#%u to bc#%u (peephole case).\n", m_currentBlock, m_currentBlock->bytecodeBegin, m_currentIndex); + dataLogF("Reascribing bytecode index of block %p from bc#%u to bc#%u (peephole case).\n", m_currentBlock, m_currentBlock->bytecodeBegin, m_currentIndex); #endif m_currentBlock->bytecodeBegin = m_currentIndex; } else { OwnPtr<BasicBlock> block = adoptPtr(new BasicBlock(m_currentIndex, m_numArguments, m_numLocals)); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Creating basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); + dataLogF("Creating basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); #endif m_currentBlock = block.get(); ASSERT(m_inlineStackTop->m_unlinkedBlocks.isEmpty() || m_graph.m_blocks[m_inlineStackTop->m_unlinkedBlocks.last().m_blockIndex]->bytecodeBegin < m_currentIndex); @@ -3697,14 +3756,14 @@ bool ByteCodeParser::parse() linkBlocks(inlineStackEntry.m_unlinkedBlocks, inlineStackEntry.m_blockLinkingTargets); m_graph.determineReachability(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Processing local variable phis.\n"); + dataLogF("Processing local variable phis.\n"); #endif m_currentProfilingIndex = m_currentIndex; processPhiStack<LocalPhiStack>(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Processing argument phis.\n"); + dataLogF("Processing argument phis.\n"); #endif processPhiStack<ArgumentPhiStack>(); diff --git a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h index a2570b7ea..8adde0598 100644 --- a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h +++ b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h @@ -210,6 +210,15 @@ public: addCallArgument(arg3); } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, GPRReg arg3) + { + resetCallArguments(); + addCallArgument(GPRInfo::callFrameRegister); + addCallArgument(arg1); + addCallArgument(arg2); + addCallArgument(arg3); + } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, TrustedImmPtr arg3) { resetCallArguments(); @@ -268,6 +277,16 @@ public: addCallArgument(arg4); } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, GPRReg arg3, TrustedImm32 arg4) + { + resetCallArguments(); + addCallArgument(GPRInfo::callFrameRegister); + addCallArgument(arg1); + addCallArgument(arg2); + addCallArgument(arg3); + addCallArgument(arg4); + } + ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, TrustedImmPtr arg2, GPRReg arg3) { resetCallArguments(); @@ -317,6 +336,23 @@ public: addCallArgument(arg4); addCallArgument(arg5); } + + ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2) + { + resetCallArguments(); + addCallArgument(GPRInfo::callFrameRegister); + addCallArgument(arg1); + addCallArgument(arg2); + } + + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3) + { + resetCallArguments(); + addCallArgument(GPRInfo::callFrameRegister); + addCallArgument(arg1); + addCallArgument(arg2); + addCallArgument(arg3); + } #endif // !NUMBER_OF_ARGUMENT_REGISTERS // These methods are suitable for any calling convention that provides for // at least 4 argument registers, e.g. X86_64, ARMv7. @@ -463,6 +499,20 @@ public: { setupTwoStubArgs<FPRInfo::argumentFPR0, FPRInfo::argumentFPR1>(arg1, arg2); } + + ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2) + { + moveDouble(arg1, FPRInfo::argumentFPR0); + move(arg2, GPRInfo::argumentGPR1); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } + + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3) + { + moveDouble(arg3, FPRInfo::argumentFPR0); + setupStubArguments(arg1, arg2); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } #elif CPU(ARM) #if CPU(ARM_HARDFP) ALWAYS_INLINE void setupArguments(FPRReg arg1) @@ -485,6 +535,20 @@ public: moveDouble(ARMRegisters::d2, FPRInfo::argumentFPR1); } } + + ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2) + { + moveDouble(arg1, FPRInfo::argumentFPR0); + move(arg2, GPRInfo::argumentGPR1); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } + + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3) + { + moveDouble(arg3, FPRInfo::argumentFPR0); + setupStubArguments(arg1, arg2); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } #else ALWAYS_INLINE void setupArguments(FPRReg arg1) { @@ -496,6 +560,21 @@ public: assembler().vmov(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, arg1); assembler().vmov(GPRInfo::argumentGPR2, GPRInfo::argumentGPR3, arg2); } + + ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2) + { + move(arg2, GPRInfo::argumentGPR3); + assembler().vmov(GPRInfo::argumentGPR1, GPRInfo::argumentGPR2, arg1); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } + + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3) + { + setupStubArguments(arg1, arg2); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + assembler().vmov(GPRInfo::argumentGPR3, GPRInfo::nonArgGPR0, arg3); + poke(GPRInfo::nonArgGPR0); + } #endif // CPU(ARM_HARDFP) #else #error "DFG JIT not supported on this platform." @@ -635,6 +714,13 @@ public: move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, GPRReg arg3) + { + setupTwoStubArgs<GPRInfo::argumentGPR1, GPRInfo::argumentGPR3>(arg1, arg3); + move(arg2, GPRInfo::argumentGPR2); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, TrustedImmPtr arg3) { move(arg1, GPRInfo::argumentGPR1); @@ -723,6 +809,12 @@ public: setupArgumentsWithExecState(arg1, arg2, arg3); } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, GPRReg arg3, TrustedImm32 arg4) + { + poke(arg4); + setupArgumentsWithExecState(arg1, arg2, arg3); + } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImmPtr arg2, TrustedImm32 arg3, GPRReg arg4) { poke(arg4); @@ -779,6 +871,12 @@ public: setupArgumentsWithExecState(arg1, arg2, arg3); } + ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, GPRReg arg2, TrustedImm32 arg3, GPRReg arg4) + { + poke(arg4); + setupArgumentsWithExecState(arg1, arg2, arg3); + } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, TrustedImm32 arg3, GPRReg arg4, GPRReg arg5) { poke(arg5, 1); @@ -786,6 +884,13 @@ public: setupArgumentsWithExecState(arg1, arg2, arg3); } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, TrustedImm32 arg3, GPRReg arg4, TrustedImm32 arg5) + { + poke(arg5, 1); + poke(arg4); + setupArgumentsWithExecState(arg1, arg2, arg3); + } + ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, GPRReg arg2, GPRReg arg3, GPRReg arg4, TrustedImmPtr arg5) { poke(arg5, 1); diff --git a/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp b/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp index 24ea0b36f..1a88066d1 100644 --- a/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp @@ -78,47 +78,47 @@ private: if (!block->cfaShouldRevisit) return; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Block #%u (bc#%u):\n", blockIndex, block->bytecodeBegin); + dataLogF(" Block #%u (bc#%u):\n", blockIndex, block->bytecodeBegin); #endif m_state.beginBasicBlock(block); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" head vars: "); + dataLogF(" head vars: "); dumpOperands(block->valuesAtHead, WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif for (unsigned i = 0; i < block->size(); ++i) { NodeIndex nodeIndex = block->at(i); if (!m_graph[nodeIndex].shouldGenerate()) continue; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" %s @%u: ", Graph::opName(m_graph[nodeIndex].op()), nodeIndex); + dataLogF(" %s @%u: ", Graph::opName(m_graph[nodeIndex].op()), nodeIndex); m_state.dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif if (!m_state.execute(i)) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Expect OSR exit.\n"); + dataLogF(" Expect OSR exit.\n"); #endif break; } } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" tail regs: "); + dataLogF(" tail regs: "); m_state.dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif m_changed |= m_state.endBasicBlock(AbstractState::MergeToSuccessors); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" tail vars: "); + dataLogF(" tail vars: "); dumpOperands(block->valuesAtTail, WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif } void performForwardCFA() { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("CFA [%u]\n", ++m_count); + dataLogF("CFA [%u]\n", ++m_count); #endif for (BlockIndex block = 0; block < m_graph.m_blocks.size(); ++block) diff --git a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp index e0d973992..d9ae4a274 100644 --- a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp @@ -66,7 +66,7 @@ public: ASSERT(m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors[0] == blockIndex); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("CFGSimplify: Jump merge on Block #%u to Block #%u.\n", + dataLogF("CFGSimplify: Jump merge on Block #%u to Block #%u.\n", blockIndex, m_graph.successor(block, 0)); #endif if (extremeLogging) @@ -76,14 +76,14 @@ public: break; } else { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Not jump merging on Block #%u to Block #%u because predecessors = ", + dataLogF("Not jump merging on Block #%u to Block #%u because predecessors = ", blockIndex, m_graph.successor(block, 0)); for (unsigned i = 0; i < m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors.size(); ++i) { if (i) - dataLog(", "); - dataLog("#%u", m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors[i]); + dataLogF(", "); + dataLogF("#%u", m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors[i]); } - dataLog(".\n"); + dataLogF(".\n"); #endif } @@ -99,14 +99,13 @@ public: case Branch: { // Branch on constant -> jettison the not-taken block and merge. - if (m_graph[m_graph[block->last()].child1()].hasConstant()) { - bool condition = - m_graph.valueOfJSConstant(m_graph[block->last()].child1().index()).toBoolean(m_graph.globalObjectFor(m_graph[block->last()].codeOrigin)->globalExec()); + if (isKnownDirection(block->cfaBranchDirection)) { + bool condition = branchCondition(block->cfaBranchDirection); BasicBlock* targetBlock = m_graph.m_blocks[ m_graph.successorForCondition(block, condition)].get(); if (targetBlock->m_predecessors.size() == 1) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("CFGSimplify: Known condition (%s) branch merge on Block #%u to Block #%u, jettisoning Block #%u.\n", + dataLogF("CFGSimplify: Known condition (%s) branch merge on Block #%u to Block #%u, jettisoning Block #%u.\n", condition ? "true" : "false", blockIndex, m_graph.successorForCondition(block, condition), m_graph.successorForCondition(block, !condition)); @@ -119,7 +118,7 @@ public: m_graph.successorForCondition(block, !condition)); } else { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("CFGSimplify: Known condition (%s) branch->jump conversion on Block #%u to Block #%u, jettisoning Block #%u.\n", + dataLogF("CFGSimplify: Known condition (%s) branch->jump conversion on Block #%u to Block #%u, jettisoning Block #%u.\n", condition ? "true" : "false", blockIndex, m_graph.successorForCondition(block, condition), m_graph.successorForCondition(block, !condition)); @@ -153,13 +152,13 @@ public: ASSERT(targetBlock->isReachable); if (targetBlock->m_predecessors.size() == 1) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("CFGSimplify: Branch to same successor merge on Block #%u to Block #%u.\n", + dataLogF("CFGSimplify: Branch to same successor merge on Block #%u to Block #%u.\n", blockIndex, targetBlockIndex); #endif mergeBlocks(blockIndex, targetBlockIndex, NoBlock); } else { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("CFGSimplify: Branch->jump conversion to same successor on Block #%u to Block #%u.\n", + dataLogF("CFGSimplify: Branch->jump conversion to same successor on Block #%u to Block #%u.\n", blockIndex, targetBlockIndex); #endif ASSERT(m_graph[block->last()].isTerminal()); @@ -180,7 +179,7 @@ public: } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Not branch simplifying on Block #%u because the successors differ and the condition is not known.\n", + dataLogF("Not branch simplifying on Block #%u because the successors differ and the condition is not known.\n", blockIndex); #endif @@ -288,22 +287,22 @@ private: if (child.op() != GetLocal) return; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Considering GetLocal at @%u, local r%d.\n", edge.index(), child.local()); + dataLogF(" Considering GetLocal at @%u, local r%d.\n", edge.index(), child.local()); #endif if (child.variableAccessData()->isCaptured()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" It's captured.\n"); + dataLogF(" It's captured.\n"); #endif return; } NodeIndex originalNodeIndex = block->variablesAtTail.operand(child.local()); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Dealing with original @%u.\n", originalNodeIndex); + dataLogF(" Dealing with original @%u.\n", originalNodeIndex); #endif ASSERT(originalNodeIndex != NoNode); Node* originalNode = &m_graph[originalNodeIndex]; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Original has local r%d.\n", originalNode->local()); + dataLogF(" Original has local r%d.\n", originalNode->local()); #endif ASSERT(child.local() == originalNode->local()); if (changeRef) @@ -328,14 +327,14 @@ private: switch (originalNode->op()) { case SetLocal: { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" It's a SetLocal.\n"); + dataLogF(" It's a SetLocal.\n"); #endif m_graph.changeIndex(edge, originalNode->child1().index(), changeRef); break; } case GetLocal: { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" It's a GetLocal.\n"); + dataLogF(" It's a GetLocal.\n"); #endif m_graph.changeIndex(edge, originalNodeIndex, changeRef); break; @@ -343,7 +342,7 @@ private: case Phi: case SetArgument: { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" It's Phi/SetArgument.\n"); + dataLogF(" It's Phi/SetArgument.\n"); #endif // Keep the GetLocal! break; @@ -381,7 +380,7 @@ private: Node& phiNode = m_graph[phiNodeIndex]; NodeIndex myNodeIndex = sourceBlock->variablesAtTail.operand(phiNode.local()); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Considering removing reference from phi @%u to @%u on local r%d:", + dataLogF("Considering removing reference from phi @%u to @%u on local r%d:", phiNodeIndex, myNodeIndex, phiNode.local()); #endif if (myNodeIndex == NoNode) { @@ -395,7 +394,7 @@ private: for (unsigned j = 0; j < AdjacencyList::Size; ++j) removePotentiallyDeadPhiReference(myNodeIndex, phiNode, j, sourceBlock->isReachable); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("\n"); + dataLogF("\n"); #endif } } @@ -403,7 +402,7 @@ private: void fixJettisonedPredecessors(BlockIndex blockIndex, BlockIndex jettisonedBlockIndex) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Fixing predecessors and phis due to jettison of Block #%u from Block #%u.\n", + dataLogF("Fixing predecessors and phis due to jettison of Block #%u from Block #%u.\n", jettisonedBlockIndex, blockIndex); #endif BasicBlock* jettisonedBlock = m_graph.m_blocks[jettisonedBlockIndex].get(); @@ -423,7 +422,7 @@ private: if (phiNode.children.child(edgeIndex).indexUnchecked() != myNodeIndex) return; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Removing reference at child %u.", edgeIndex); + dataLogF(" Removing reference at child %u.", edgeIndex); #endif if (changeRef && phiNode.shouldGenerate()) m_graph.deref(myNodeIndex); @@ -711,7 +710,7 @@ private: bool changeRef = phiNode.shouldGenerate(); OperandSubstitution substitution = substitutions.operand(phiNode.local()); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Performing operand substitution @%u -> @%u.\n", + dataLogF(" Performing operand substitution @%u -> @%u.\n", substitution.oldChild, substitution.newChild); #endif if (!phiNode.child1()) @@ -730,6 +729,7 @@ private: } firstBlock->valuesAtTail = secondBlock->valuesAtTail; + firstBlock->cfaBranchDirection = secondBlock->cfaBranchDirection; m_graph.m_blocks[secondBlockIndex].clear(); } diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp index 19051c174..36acb2c21 100644 --- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp @@ -79,7 +79,7 @@ private: result++; ASSERT(result <= m_indexInBlock); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" limit %u: ", result); + dataLogF(" limit %u: ", result); #endif return result; } @@ -372,7 +372,7 @@ private: return NoNode; } - bool checkFunctionElimination(JSFunction* function, NodeIndex child1) + bool checkFunctionElimination(JSCell* function, NodeIndex child1) { for (unsigned i = endIndexForPureCSE(); i--;) { NodeIndex index = m_currentBlock->at(i); @@ -970,7 +970,7 @@ private: return false; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Replacing @%u -> @%u", m_compileIndex, replacement); + dataLogF(" Replacing @%u -> @%u", m_compileIndex, replacement); #endif Node& node = m_graph[m_compileIndex]; @@ -988,7 +988,7 @@ private: void eliminate() { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Eliminating @%u", m_compileIndex); + dataLogF(" Eliminating @%u", m_compileIndex); #endif Node& node = m_graph[m_compileIndex]; @@ -1029,7 +1029,7 @@ private: return; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" %s @%u: ", Graph::opName(m_graph[m_compileIndex].op()), m_compileIndex); + dataLogF(" %s @%u: ", Graph::opName(m_graph[m_compileIndex].op()), m_compileIndex); #endif // NOTE: there are some nodes that we deliberately don't CSE even though we @@ -1043,6 +1043,10 @@ private: switch (node.op()) { + case Identity: + setReplacement(node.child1().index()); + break; + // Handle the pure nodes. These nodes never have any side-effects. case BitAnd: case BitOr: @@ -1313,7 +1317,7 @@ private: m_lastSeen[node.op()] = m_indexInBlock; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("\n"); + dataLogF("\n"); #endif } diff --git a/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h b/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h index 46d5f44cb..03713b6c5 100644 --- a/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h +++ b/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h @@ -61,7 +61,7 @@ protected: jit->silentSpill(m_plans[i]); jit->callOperation(m_function, m_resultGPR, m_structure, m_size); GPRReg canTrample = SpeculativeJIT::pickCanTrample(m_resultGPR); - for (unsigned i = 0; i < m_plans.size(); ++i) + for (unsigned i = m_plans.size(); i--;) jit->silentFill(m_plans[i], canTrample); jit->m_jit.loadPtr(MacroAssembler::Address(m_resultGPR, JSObject::butterflyOffset()), m_storageGPR); jumpTo(jit); @@ -106,7 +106,7 @@ protected: done.link(&jit->m_jit); jit->callOperation(m_function, m_resultGPR, scratchGPR, m_sizeGPR); GPRReg canTrample = SpeculativeJIT::pickCanTrample(m_resultGPR); - for (unsigned i = 0; i < m_plans.size(); ++i) + for (unsigned i = m_plans.size(); i--;) jit->silentFill(m_plans[i], canTrample); jumpTo(jit); } diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp index 910c3d986..869751372 100644 --- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp +++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp @@ -38,7 +38,7 @@ static inline void debugFail(CodeBlock* codeBlock, OpcodeID opcodeID, bool resul { ASSERT_UNUSED(result, !result); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Cannot handle code block %p because of opcode %s.\n", codeBlock, opcodeNames[opcodeID]); + dataLogF("Cannot handle code block %p because of opcode %s.\n", codeBlock, opcodeNames[opcodeID]); #else UNUSED_PARAM(codeBlock); UNUSED_PARAM(opcodeID); @@ -51,10 +51,10 @@ static inline void debugFail(CodeBlock* codeBlock, OpcodeID opcodeID, Capability ASSERT(result != CanCompile); #if DFG_ENABLE(DEBUG_VERBOSE) if (result == CannotCompile) - dataLog("Cannot handle code block %p because of opcode %s.\n", codeBlock, opcodeNames[opcodeID]); + dataLogF("Cannot handle code block %p because of opcode %s.\n", codeBlock, opcodeNames[opcodeID]); else { ASSERT(result == ShouldProfile); - dataLog("Cannot compile code block %p because of opcode %s, but inlining might be possible.\n", codeBlock, opcodeNames[opcodeID]); + dataLogF("Cannot compile code block %p because of opcode %s, but inlining might be possible.\n", codeBlock, opcodeNames[opcodeID]); } #else UNUSED_PARAM(codeBlock); diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.h b/Source/JavaScriptCore/dfg/DFGCapabilities.h index 1f9778efe..a89c697f6 100644 --- a/Source/JavaScriptCore/dfg/DFGCapabilities.h +++ b/Source/JavaScriptCore/dfg/DFGCapabilities.h @@ -116,6 +116,7 @@ inline CapabilityLevel canCompileOpcode(OpcodeID opcodeID, CodeBlock*, Instructi case op_enter: case op_convert_this: case op_create_this: + case op_get_callee: case op_bitand: case op_bitor: case op_bitxor: diff --git a/Source/JavaScriptCore/dfg/DFGCommon.h b/Source/JavaScriptCore/dfg/DFGCommon.h index c3726ed85..2c0556d60 100644 --- a/Source/JavaScriptCore/dfg/DFGCommon.h +++ b/Source/JavaScriptCore/dfg/DFGCommon.h @@ -135,11 +135,6 @@ enum NoResultTag { NoResult }; enum OptimizationFixpointState { BeforeFixpoint, FixpointNotConverged, FixpointConverged }; -inline bool shouldShowDisassembly() -{ - return Options::showDisassembly() || Options::showDFGDisassembly(); -} - } } // namespace JSC::DFG #endif // ENABLE(DFG_JIT) @@ -150,6 +145,16 @@ namespace JSC { namespace DFG { enum CapabilityLevel { CannotCompile, ShouldProfile, CanCompile, CapabilityLevelNotSet }; +// Unconditionally disable DFG disassembly support if the DFG is not compiled in. +inline bool shouldShowDisassembly() +{ +#if ENABLE(DFG_JIT) + return Options::showDisassembly() || Options::showDFGDisassembly(); +#else + return false; +#endif +} + } } // namespace JSC::DFG #endif // DFGCommon_h diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp index 43aa2c007..2221954b5 100644 --- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp @@ -33,6 +33,8 @@ #include "DFGGraph.h" #include "DFGInsertionSet.h" #include "DFGPhase.h" +#include "GetByIdStatus.h" +#include "PutByIdStatus.h" namespace JSC { namespace DFG { @@ -65,7 +67,7 @@ private: bool foldConstants(BlockIndex blockIndex) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Constant folding considering Block #%u.\n", blockIndex); + dataLogF("Constant folding considering Block #%u.\n", blockIndex); #endif BasicBlock* block = m_graph.m_blocks[blockIndex].get(); bool changed = false; @@ -109,14 +111,16 @@ private: StructureAbstractValue& structureValue = value.m_futurePossibleStructure; if (structureValue.isSubsetOf(set) && structureValue.hasSingleton() - && isCellSpeculation(value.m_type)) + && isCellSpeculation(value.m_type)) { node.convertToStructureTransitionWatchpoint(structureValue.singleton()); + changed = true; + } break; } case CheckArray: case Arrayify: { - if (!node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))) + if (!node.arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node.child1()))) break; ASSERT(node.refCount() == 1); node.setOpAndDefaultFlags(Phantom); @@ -124,6 +128,206 @@ private: break; } + case CheckFunction: { + if (m_state.forNode(node.child1()).value() != node.function()) + break; + node.setOpAndDefaultFlags(Phantom); + eliminated = true; + break; + } + + case ConvertThis: { + if (!isObjectSpeculation(m_state.forNode(node.child1()).m_type)) + break; + node.setOpAndDefaultFlags(Identity); + changed = true; + break; + } + + case GetById: + case GetByIdFlush: { + CodeOrigin codeOrigin = node.codeOrigin; + NodeIndex child = node.child1().index(); + unsigned identifierNumber = node.identifierNumber(); + + if (!isCellSpeculation(m_graph[child].prediction())) + break; + + Structure* structure = m_state.forNode(child).bestProvenStructure(); + if (!structure) + break; + + bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton(); + + GetByIdStatus status = GetByIdStatus::computeFor( + globalData(), structure, codeBlock()->identifier(identifierNumber)); + + if (!status.isSimple()) + break; + + ASSERT(status.structureSet().size() == 1); + ASSERT(status.chain().isEmpty()); + ASSERT(status.structureSet().singletonStructure() == structure); + + // Now before we do anything else, push the CFA forward over the GetById + // and make sure we signal to the loop that it should continue and not + // do any eliminations. + m_state.execute(indexInBlock); + eliminated = true; + + if (needsWatchpoint) { + ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure))); + m_graph[child].ref(); + Node watchpoint(StructureTransitionWatchpoint, codeOrigin, OpInfo(structure), child); + watchpoint.ref(); + NodeIndex watchpointIndex = m_graph.size(); + m_graph.append(watchpoint); + m_insertionSet.append(indexInBlock, watchpointIndex); + } + + NodeIndex propertyStorageIndex; + + m_graph[child].ref(); + if (isInlineOffset(status.offset())) + propertyStorageIndex = child; + else { + Node getButterfly(GetButterfly, codeOrigin, child); + getButterfly.ref(); + propertyStorageIndex = m_graph.size(); + m_graph.append(getButterfly); + m_insertionSet.append(indexInBlock, propertyStorageIndex); + } + + m_graph[nodeIndex].convertToGetByOffset(m_graph.m_storageAccessData.size(), propertyStorageIndex); + + StorageAccessData storageAccessData; + storageAccessData.offset = indexRelativeToBase(status.offset()); + storageAccessData.identifierNumber = identifierNumber; + m_graph.m_storageAccessData.append(storageAccessData); + break; + } + + case PutById: + case PutByIdDirect: { + CodeOrigin codeOrigin = node.codeOrigin; + NodeIndex child = node.child1().index(); + unsigned identifierNumber = node.identifierNumber(); + + Structure* structure = m_state.forNode(child).bestProvenStructure(); + if (!structure) + break; + + bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton(); + + PutByIdStatus status = PutByIdStatus::computeFor( + globalData(), + m_graph.globalObjectFor(codeOrigin), + structure, + codeBlock()->identifier(identifierNumber), + node.op() == PutByIdDirect); + + if (!status.isSimpleReplace() && !status.isSimpleTransition()) + break; + + ASSERT(status.oldStructure() == structure); + + // Now before we do anything else, push the CFA forward over the PutById + // and make sure we signal to the loop that it should continue and not + // do any eliminations. + m_state.execute(indexInBlock); + eliminated = true; + + if (needsWatchpoint) { + ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure))); + m_graph[child].ref(); + Node watchpoint(StructureTransitionWatchpoint, codeOrigin, OpInfo(structure), child); + watchpoint.ref(); + NodeIndex watchpointIndex = m_graph.size(); + m_graph.append(watchpoint); + m_insertionSet.append(indexInBlock, watchpointIndex); + } + + StructureTransitionData* transitionData = 0; + if (status.isSimpleTransition()) { + transitionData = m_graph.addStructureTransitionData( + StructureTransitionData(structure, status.newStructure())); + + if (node.op() == PutById) { + if (!structure->storedPrototype().isNull()) { + addStructureTransitionCheck( + codeOrigin, indexInBlock, + structure->storedPrototype().asCell()); + } + + for (WriteBarrier<Structure>* it = status.structureChain()->head(); *it; ++it) { + JSValue prototype = (*it)->storedPrototype(); + if (prototype.isNull()) + continue; + ASSERT(prototype.isCell()); + addStructureTransitionCheck( + codeOrigin, indexInBlock, prototype.asCell()); + } + } + } + + NodeIndex propertyStorageIndex; + + m_graph[child].ref(); + if (isInlineOffset(status.offset())) + propertyStorageIndex = child; + else if (status.isSimpleReplace() || structure->outOfLineCapacity() == status.newStructure()->outOfLineCapacity()) { + Node getButterfly(GetButterfly, codeOrigin, child); + getButterfly.ref(); + propertyStorageIndex = m_graph.size(); + m_graph.append(getButterfly); + m_insertionSet.append(indexInBlock, propertyStorageIndex); + } else if (!structure->outOfLineCapacity()) { + ASSERT(status.newStructure()->outOfLineCapacity()); + ASSERT(!isInlineOffset(status.offset())); + Node allocateStorage(AllocatePropertyStorage, codeOrigin, OpInfo(transitionData), child); + allocateStorage.ref(); // Once for the use. + allocateStorage.ref(); // Twice because it's must-generate. + propertyStorageIndex = m_graph.size(); + m_graph.append(allocateStorage); + m_insertionSet.append(indexInBlock, propertyStorageIndex); + } else { + ASSERT(structure->outOfLineCapacity()); + ASSERT(status.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity()); + ASSERT(!isInlineOffset(status.offset())); + + Node getButterfly(GetButterfly, codeOrigin, child); + getButterfly.ref(); + NodeIndex getButterflyIndex = m_graph.size(); + m_graph.append(getButterfly); + m_insertionSet.append(indexInBlock, getButterflyIndex); + + m_graph[child].ref(); + Node reallocateStorage(ReallocatePropertyStorage, codeOrigin, OpInfo(transitionData), child, getButterflyIndex); + reallocateStorage.ref(); // Once for the use. + reallocateStorage.ref(); // Twice because it's must-generate. + propertyStorageIndex = m_graph.size(); + m_graph.append(reallocateStorage); + m_insertionSet.append(indexInBlock, propertyStorageIndex); + } + + if (status.isSimpleTransition()) { + m_graph[child].ref(); + Node putStructure(PutStructure, codeOrigin, OpInfo(transitionData), child); + putStructure.ref(); + NodeIndex putStructureIndex = m_graph.size(); + m_graph.append(putStructure); + m_insertionSet.append(indexInBlock, putStructureIndex); + } + + m_graph[nodeIndex].convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorageIndex); + + StorageAccessData storageAccessData; + storageAccessData.offset = indexRelativeToBase(status.offset()); + storageAccessData.identifierNumber = identifierNumber; + m_graph.m_storageAccessData.append(storageAccessData); + break; + } + default: break; } @@ -213,6 +417,31 @@ private: return changed; } + void addStructureTransitionCheck(CodeOrigin codeOrigin, unsigned indexInBlock, JSCell* cell) + { + Node weakConstant(WeakJSConstant, codeOrigin, OpInfo(cell)); + weakConstant.ref(); + weakConstant.predict(speculationFromValue(cell)); + NodeIndex weakConstantIndex = m_graph.size(); + m_graph.append(weakConstant); + m_insertionSet.append(indexInBlock, weakConstantIndex); + + if (cell->structure()->transitionWatchpointSetIsStillValid()) { + Node watchpoint(StructureTransitionWatchpoint, codeOrigin, OpInfo(cell->structure()), weakConstantIndex); + watchpoint.ref(); + NodeIndex watchpointIndex = m_graph.size(); + m_graph.append(watchpoint); + m_insertionSet.append(indexInBlock, watchpointIndex); + return; + } + + Node check(CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(cell->structure())), weakConstantIndex); + check.ref(); + NodeIndex checkIndex = m_graph.size(); + m_graph.append(check); + m_insertionSet.append(indexInBlock, checkIndex); + } + // This is necessary because the CFA may reach conclusions about constants based on its // assumption that certain code must exit, but then those constants may lead future // reexecutions of the CFA to believe that the same code will now no longer exit. Thus @@ -225,7 +454,7 @@ private: bool changed = false; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Painting unreachable code in Block #%u.\n", blockIndex); + dataLogF("Painting unreachable code in Block #%u.\n", blockIndex); #endif BasicBlock* block = m_graph.m_blocks[blockIndex].get(); m_state.beginBasicBlock(block); diff --git a/Source/JavaScriptCore/dfg/DFGDisassembler.cpp b/Source/JavaScriptCore/dfg/DFGDisassembler.cpp index cfbb936b8..654824196 100644 --- a/Source/JavaScriptCore/dfg/DFGDisassembler.cpp +++ b/Source/JavaScriptCore/dfg/DFGDisassembler.cpp @@ -43,8 +43,8 @@ void Disassembler::dump(LinkBuffer& linkBuffer) { m_graph.m_dominators.computeIfNecessary(m_graph); - dataLog("Generated JIT code for DFG CodeBlock %p, instruction count = %u:\n", m_graph.m_codeBlock, m_graph.m_codeBlock->instructionCount()); - dataLog(" Code at [%p, %p):\n", linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize()); + dataLogF("Generated JIT code for DFG CodeBlock %p, instruction count = %u:\n", m_graph.m_codeBlock, m_graph.m_codeBlock->instructionCount()); + dataLogF(" Code at [%p, %p):\n", linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize()); const char* prefix = " "; const char* disassemblyPrefix = " "; @@ -82,7 +82,7 @@ void Disassembler::dump(LinkBuffer& linkBuffer) } } dumpDisassembly(disassemblyPrefix, linkBuffer, previousLabel, m_endOfMainPath, lastNodeIndex); - dataLog("%s(End Of Main Path)\n", prefix); + dataLogF("%s(End Of Main Path)\n", prefix); dumpDisassembly(disassemblyPrefix, linkBuffer, previousLabel, m_endOfCode, NoNode); } @@ -104,10 +104,7 @@ void Disassembler::dumpDisassembly(const char* prefix, LinkBuffer& linkBuffer, M CodeLocationLabel end = linkBuffer.locationOf(currentLabel); previousLabel = currentLabel; ASSERT(bitwise_cast<uintptr_t>(end.executableAddress()) >= bitwise_cast<uintptr_t>(start.executableAddress())); - if (tryToDisassemble(start, bitwise_cast<uintptr_t>(end.executableAddress()) - bitwise_cast<uintptr_t>(start.executableAddress()), prefixBuffer.get(), WTF::dataFile())) - return; - - dataLog("%s disassembly not available for range %p...%p\n", prefixBuffer.get(), start.executableAddress(), end.executableAddress()); + disassemble(start, bitwise_cast<uintptr_t>(end.executableAddress()) - bitwise_cast<uintptr_t>(start.executableAddress()), prefixBuffer.get(), WTF::dataFile()); } } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGDriver.cpp b/Source/JavaScriptCore/dfg/DFGDriver.cpp index eb68fa344..8645c6dce 100644 --- a/Source/JavaScriptCore/dfg/DFGDriver.cpp +++ b/Source/JavaScriptCore/dfg/DFGDriver.cpp @@ -72,7 +72,7 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo return false; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("DFG compiling code block %p(%p) for executable %p, number of instructions = %u.\n", codeBlock, codeBlock->alternative(), codeBlock->ownerExecutable(), codeBlock->instructionCount()); + dataLogF("DFG compiling code block %p(%p) for executable %p, number of instructions = %u.\n", codeBlock, codeBlock->alternative(), codeBlock->ownerExecutable(), codeBlock->instructionCount()); #endif // Derive our set of must-handle values. The compilation must be at least conservative @@ -119,7 +119,7 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo dfg.m_fixpointState = FixpointNotConverged; for (;; ++cnt) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("DFG beginning optimization fixpoint iteration #%u.\n", cnt); + dataLogF("DFG beginning optimization fixpoint iteration #%u.\n", cnt); #endif bool changed = false; performCFA(dfg); @@ -135,13 +135,13 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo dfg.m_fixpointState = FixpointConverged; performCSE(dfg); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("DFG optimization fixpoint converged in %u iterations.\n", cnt); + dataLogF("DFG optimization fixpoint converged in %u iterations.\n", cnt); #endif performVirtualRegisterAllocation(dfg); GraphDumpMode modeForFinalValidate = DumpGraph; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Graph after optimization:\n"); + dataLogF("Graph after optimization:\n"); dfg.dump(); modeForFinalValidate = DontDumpGraph; #endif diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index 5a76aa8df..1ba40def3 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -69,7 +69,7 @@ private: NodeType op = node.op(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" %s @%u: ", Graph::opName(op), m_compileIndex); + dataLogF(" %s @%u: ", Graph::opName(op), m_compileIndex); #endif switch (op) { @@ -134,6 +134,17 @@ private: m_graph[node.child2()].prediction())); blessArrayOperation(node.child1(), node.child2(), 2); + + Node* nodePtr = &m_graph[m_compileIndex]; + ArrayMode arrayMode = nodePtr->arrayMode(); + if (arrayMode.type() == Array::Double + && arrayMode.arrayClass() == Array::OriginalArray + && arrayMode.speculation() == Array::InBounds + && arrayMode.conversion() == Array::AsIs + && m_graph.globalObjectFor(nodePtr->codeOrigin)->arrayPrototypeChainIsSane() + && !(nodePtr->flags() & NodeUsedAsOther)) + nodePtr->setArrayMode(arrayMode.withSpeculation(Array::SaneChain)); + break; } case StringCharAt: @@ -145,7 +156,30 @@ private: } case ArrayPush: { + // May need to refine the array mode in case the value prediction contravenes + // the array prediction. For example, we may have evidence showing that the + // array is in Int32 mode, but the value we're storing is likely to be a double. + // Then we should turn this into a conversion to Double array followed by the + // push. On the other hand, we absolutely don't want to refine based on the + // base prediction. If it has non-cell garbage in it, then we want that to be + // ignored. That's because ArrayPush can't handle any array modes that aren't + // array-related - so if refine() turned this into a "Generic" ArrayPush then + // that would break things. + node.setArrayMode( + node.arrayMode().refine( + m_graph[node.child1()].prediction() & SpecCell, + SpecInt32, + m_graph[node.child2()].prediction())); blessArrayOperation(node.child1(), node.child2(), 2); + + Node* nodePtr = &m_graph[m_compileIndex]; + switch (nodePtr->arrayMode().type()) { + case Array::Double: + fixDoubleEdge(1); + break; + default: + break; + } break; } @@ -236,7 +270,7 @@ private: case ValueAdd: { if (m_graph.addShouldSpeculateInteger(node)) break; - if (!Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()])) + if (!Node::shouldSpeculateNumberExpectingDefined(m_graph[node.child1()], m_graph[node.child2()])) break; fixDoubleEdge(0); fixDoubleEdge(1); @@ -262,7 +296,7 @@ private: case ArithMin: case ArithMax: case ArithMod: { - if (Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child2()]) + if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()]) && node.canSpeculateInteger()) break; fixDoubleEdge(0); @@ -279,7 +313,7 @@ private: } case ArithDiv: { - if (Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child2()]) + if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()]) && node.canSpeculateInteger()) { if (isX86()) break; @@ -307,7 +341,7 @@ private: } case ArithAbs: { - if (m_graph[node.child1()].shouldSpeculateInteger() + if (m_graph[node.child1()].shouldSpeculateIntegerForArithmetic() && node.canSpeculateInteger()) break; fixDoubleEdge(0); @@ -328,13 +362,17 @@ private: node.setArrayMode( node.arrayMode().refine( m_graph[child1].prediction(), - m_graph[child2].prediction())); + m_graph[child2].prediction(), + m_graph[child3].prediction())); blessArrayOperation(child1, child2, 3); Node* nodePtr = &m_graph[m_compileIndex]; switch (nodePtr->arrayMode().modeForPut().type()) { + case Array::Double: + fixDoubleEdge(2); + break; case Array::Int8Array: case Array::Int16Array: case Array::Int32Array: @@ -355,16 +393,29 @@ private: break; } + case NewArray: { + for (unsigned i = m_graph.varArgNumChildren(node); i--;) { + node.setIndexingType( + leastUpperBoundOfIndexingTypeAndType( + node.indexingType(), m_graph[m_graph.varArgChild(node, i)].prediction())); + } + if (node.indexingType() == ArrayWithDouble) { + for (unsigned i = m_graph.varArgNumChildren(node); i--;) + fixDoubleEdge(i); + } + break; + } + default: break; } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) if (!(node.flags() & NodeHasVarArgs)) { - dataLog("new children: "); + dataLogF("new children: "); node.dumpChildren(WTF::dataFile()); } - dataLog("\n"); + dataLogF("\n"); #endif } @@ -384,29 +435,12 @@ private: m_graph.ref(array); + Structure* structure = arrayMode.originalArrayStructure(m_graph, codeOrigin); + if (arrayMode.doesConversion()) { if (index != NoNode) m_graph.ref(index); - 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(); @@ -421,11 +455,19 @@ private: 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); + if (structure) { + Node checkStructure(CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(structure)), array); + checkStructure.ref(); + NodeIndex checkStructureIndex = m_graph.size(); + m_graph.append(checkStructure); + m_insertionSet.append(m_indexInBlock, checkStructureIndex); + } 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); + } } if (!storageCheck(arrayMode)) @@ -506,7 +548,7 @@ private: NodeIndex resultIndex = (NodeIndex)m_graph.size(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("(replacing @%u->@%u with @%u->@%u) ", + dataLogF("(replacing @%u->@%u with @%u->@%u) ", m_compileIndex, edge.index(), m_compileIndex, resultIndex); #endif diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp index 8e8817f81..19587ba64 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.cpp +++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp @@ -98,7 +98,7 @@ const char* Graph::nameOfVariableAccessData(VariableAccessData* variableAccessDa static void printWhiteSpace(unsigned amount) { while (amount-- > 0) - dataLog(" "); + dataLogF(" "); } void Graph::dumpCodeOrigin(const char* prefix, NodeIndex prevNodeIndex, NodeIndex nodeIndex) @@ -124,16 +124,16 @@ void Graph::dumpCodeOrigin(const char* prefix, NodeIndex prevNodeIndex, NodeInde // Print the pops. for (unsigned i = previousInlineStack.size(); i-- > indexOfDivergence;) { - dataLog("%s", prefix); + dataLogF("%s", prefix); printWhiteSpace(i * 2); - dataLog("<-- %p\n", previousInlineStack[i].inlineCallFrame->executable.get()); + dataLogF("<-- %p\n", previousInlineStack[i].inlineCallFrame->executable.get()); } // Print the pushes. for (unsigned i = indexOfDivergence; i < currentInlineStack.size(); ++i) { - dataLog("%s", prefix); + dataLogF("%s", prefix); printWhiteSpace(i * 2); - dataLog("--> %p\n", currentInlineStack[i].inlineCallFrame->executable.get()); + dataLogF("--> %p\n", currentInlineStack[i].inlineCallFrame->executable.get()); } } @@ -158,7 +158,7 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) if (mustGenerate) --refCount; - dataLog("%s", prefix); + dataLogF("%s", prefix); printNodeWhiteSpace(node); // Example/explanation of dataflow dump output @@ -178,22 +178,22 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) // $# - the index in the CodeBlock of a constant { for numeric constants the value is displayed | for integers, in both decimal and hex }. // id# - the index in the CodeBlock of an identifier { if codeBlock is passed to dump(), the string representation is displayed }. // var# - the index of a var on the global object, used by GetGlobalVar/PutGlobalVar operations. - dataLog("% 4d:%s<%c%u:", (int)nodeIndex, skipped ? " skipped " : " ", mustGenerate ? '!' : ' ', refCount); + dataLogF("% 4d:%s<%c%u:", (int)nodeIndex, skipped ? " skipped " : " ", mustGenerate ? '!' : ' ', refCount); if (node.hasResult() && !skipped && node.hasVirtualRegister()) - dataLog("%u", node.virtualRegister()); + dataLogF("%u", node.virtualRegister()); else - dataLog("-"); - dataLog(">\t%s(", opName(op)); + dataLogF("-"); + dataLogF(">\t%s(", opName(op)); bool hasPrinted = false; if (node.flags() & NodeHasVarArgs) { for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) { if (hasPrinted) - dataLog(", "); + dataLogF(", "); else hasPrinted = true; if (!m_varArgChildren[childIdx]) continue; - dataLog("%s@%u%s", + dataLogF("%s@%u%s", useKindToString(m_varArgChildren[childIdx].useKind()), m_varArgChildren[childIdx].index(), speculationToAbbreviatedString( @@ -201,19 +201,19 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) } } else { if (!!node.child1()) { - dataLog("%s@%u%s", + dataLogF("%s@%u%s", useKindToString(node.child1().useKind()), node.child1().index(), speculationToAbbreviatedString(at(node.child1()).prediction())); } if (!!node.child2()) { - dataLog(", %s@%u%s", + dataLogF(", %s@%u%s", useKindToString(node.child2().useKind()), node.child2().index(), speculationToAbbreviatedString(at(node.child2()).prediction())); } if (!!node.child3()) { - dataLog(", %s@%u%s", + dataLogF(", %s@%u%s", useKindToString(node.child3().useKind()), node.child3().index(), speculationToAbbreviatedString(at(node.child3()).prediction())); @@ -222,47 +222,51 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) } if (strlen(nodeFlagsAsString(node.flags()))) { - dataLog("%s%s", hasPrinted ? ", " : "", nodeFlagsAsString(node.flags())); + dataLogF("%s%s", hasPrinted ? ", " : "", nodeFlagsAsString(node.flags())); hasPrinted = true; } if (node.hasArrayMode()) { - dataLog("%s%s", hasPrinted ? ", " : "", node.arrayMode().toString()); + dataLogF("%s%s", hasPrinted ? ", " : "", node.arrayMode().toString()); hasPrinted = true; } if (node.hasVarNumber()) { - dataLog("%svar%u", hasPrinted ? ", " : "", node.varNumber()); + dataLogF("%svar%u", hasPrinted ? ", " : "", node.varNumber()); hasPrinted = true; } if (node.hasRegisterPointer()) { - dataLog( + dataLogF( "%sglobal%u(%p)", hasPrinted ? ", " : "", globalObjectFor(node.codeOrigin)->findRegisterIndex(node.registerPointer()), node.registerPointer()); hasPrinted = true; } if (node.hasIdentifier()) { - dataLog("%sid%u{%s}", hasPrinted ? ", " : "", node.identifierNumber(), m_codeBlock->identifier(node.identifierNumber()).string().utf8().data()); + dataLogF("%sid%u{%s}", hasPrinted ? ", " : "", node.identifierNumber(), m_codeBlock->identifier(node.identifierNumber()).string().utf8().data()); hasPrinted = true; } if (node.hasStructureSet()) { for (size_t i = 0; i < node.structureSet().size(); ++i) { - dataLog("%sstruct(%p: %s)", hasPrinted ? ", " : "", node.structureSet()[i], indexingTypeToString(node.structureSet()[i]->indexingType())); + dataLogF("%sstruct(%p: %s)", hasPrinted ? ", " : "", node.structureSet()[i], indexingTypeToString(node.structureSet()[i]->indexingType())); hasPrinted = true; } } if (node.hasStructure()) { - dataLog("%sstruct(%p: %s)", hasPrinted ? ", " : "", node.structure(), indexingTypeToString(node.structure()->indexingType())); + dataLogF("%sstruct(%p: %s)", hasPrinted ? ", " : "", node.structure(), indexingTypeToString(node.structure()->indexingType())); hasPrinted = true; } if (node.hasStructureTransitionData()) { - dataLog("%sstruct(%p -> %p)", hasPrinted ? ", " : "", node.structureTransitionData().previousStructure, node.structureTransitionData().newStructure); + dataLogF("%sstruct(%p -> %p)", hasPrinted ? ", " : "", node.structureTransitionData().previousStructure, node.structureTransitionData().newStructure); + hasPrinted = true; + } + if (node.hasFunction()) { + dataLogF("%s%p", hasPrinted ? ", " : "", node.function()); hasPrinted = true; } if (node.hasStorageAccessData()) { StorageAccessData& storageAccessData = m_storageAccessData[node.storageAccessDataIndex()]; - dataLog("%sid%u{%s}", hasPrinted ? ", " : "", storageAccessData.identifierNumber, m_codeBlock->identifier(storageAccessData.identifierNumber).string().utf8().data()); + dataLogF("%sid%u{%s}", hasPrinted ? ", " : "", storageAccessData.identifierNumber, m_codeBlock->identifier(storageAccessData.identifierNumber).string().utf8().data()); - dataLog(", %lu", static_cast<unsigned long>(storageAccessData.offset)); + dataLogF(", %lu", static_cast<unsigned long>(storageAccessData.offset)); hasPrinted = true; } ASSERT(node.hasVariableAccessData() == node.hasLocal()); @@ -270,100 +274,105 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) VariableAccessData* variableAccessData = node.variableAccessData(); int operand = variableAccessData->operand(); if (operandIsArgument(operand)) - dataLog("%sarg%u(%s)", hasPrinted ? ", " : "", operandToArgument(operand), nameOfVariableAccessData(variableAccessData)); + dataLogF("%sarg%u(%s)", hasPrinted ? ", " : "", operandToArgument(operand), nameOfVariableAccessData(variableAccessData)); else - dataLog("%sr%u(%s)", hasPrinted ? ", " : "", operand, nameOfVariableAccessData(variableAccessData)); + dataLogF("%sr%u(%s)", hasPrinted ? ", " : "", operand, nameOfVariableAccessData(variableAccessData)); hasPrinted = true; } if (node.hasConstantBuffer()) { if (hasPrinted) - dataLog(", "); - dataLog("%u:[", node.startConstant()); + dataLogF(", "); + dataLogF("%u:[", node.startConstant()); for (unsigned i = 0; i < node.numConstants(); ++i) { if (i) - dataLog(", "); - dataLog("%s", m_codeBlock->constantBuffer(node.startConstant())[i].description()); + dataLogF(", "); + dataLogF("%s", m_codeBlock->constantBuffer(node.startConstant())[i].description()); } - dataLog("]"); + dataLogF("]"); hasPrinted = true; } + if (node.hasIndexingType()) { + if (hasPrinted) + dataLogF(", "); + dataLogF("%s", indexingTypeToString(node.indexingType())); + } if (op == JSConstant) { - dataLog("%s$%u", hasPrinted ? ", " : "", node.constantNumber()); + dataLogF("%s$%u", hasPrinted ? ", " : "", node.constantNumber()); JSValue value = valueOfJSConstant(nodeIndex); - dataLog(" = %s", value.description()); + dataLogF(" = %s", value.description()); hasPrinted = true; } if (op == WeakJSConstant) { - dataLog("%s%p", hasPrinted ? ", " : "", node.weakConstant()); + dataLogF("%s%p", hasPrinted ? ", " : "", node.weakConstant()); hasPrinted = true; } if (node.isBranch() || node.isJump()) { - dataLog("%sT:#%u", hasPrinted ? ", " : "", node.takenBlockIndex()); + dataLogF("%sT:#%u", hasPrinted ? ", " : "", node.takenBlockIndex()); hasPrinted = true; } if (node.isBranch()) { - dataLog("%sF:#%u", hasPrinted ? ", " : "", node.notTakenBlockIndex()); + dataLogF("%sF:#%u", hasPrinted ? ", " : "", node.notTakenBlockIndex()); hasPrinted = true; } - dataLog("%sbc#%u", hasPrinted ? ", " : "", node.codeOrigin.bytecodeIndex); + dataLogF("%sbc#%u", hasPrinted ? ", " : "", node.codeOrigin.bytecodeIndex); hasPrinted = true; (void)hasPrinted; - dataLog(")"); + dataLogF(")"); if (!skipped) { if (node.hasVariableAccessData()) - dataLog(" predicting %s%s", speculationToString(node.variableAccessData()->prediction()), node.variableAccessData()->shouldUseDoubleFormat() ? ", forcing double" : ""); + dataLogF(" predicting %s%s", speculationToString(node.variableAccessData()->prediction()), node.variableAccessData()->shouldUseDoubleFormat() ? ", forcing double" : ""); else if (node.hasHeapPrediction()) - dataLog(" predicting %s", speculationToString(node.getHeapPrediction())); + dataLogF(" predicting %s", speculationToString(node.getHeapPrediction())); } - dataLog("\n"); + dataLogF("\n"); } void Graph::dumpBlockHeader(const char* prefix, BlockIndex blockIndex, PhiNodeDumpMode phiNodeDumpMode) { BasicBlock* block = m_blocks[blockIndex].get(); - dataLog("%sBlock #%u (bc#%u): %s%s\n", prefix, (int)blockIndex, block->bytecodeBegin, block->isReachable ? "" : " (skipped)", block->isOSRTarget ? " (OSR target)" : ""); - dataLog("%s Predecessors:", prefix); + dataLogF("%sBlock #%u (bc#%u): %s%s\n", prefix, (int)blockIndex, block->bytecodeBegin, block->isReachable ? "" : " (skipped)", block->isOSRTarget ? " (OSR target)" : ""); + dataLogF("%s Predecessors:", prefix); for (size_t i = 0; i < block->m_predecessors.size(); ++i) - dataLog(" #%u", block->m_predecessors[i]); - dataLog("\n"); + dataLogF(" #%u", block->m_predecessors[i]); + dataLogF("\n"); if (m_dominators.isValid()) { - dataLog("%s Dominated by:", prefix); + dataLogF("%s Dominated by:", prefix); for (size_t i = 0; i < m_blocks.size(); ++i) { if (!m_dominators.dominates(i, blockIndex)) continue; - dataLog(" #%lu", static_cast<unsigned long>(i)); + dataLogF(" #%lu", static_cast<unsigned long>(i)); } - dataLog("\n"); - dataLog("%s Dominates:", prefix); + dataLogF("\n"); + dataLogF("%s Dominates:", prefix); for (size_t i = 0; i < m_blocks.size(); ++i) { if (!m_dominators.dominates(blockIndex, i)) continue; - dataLog(" #%lu", static_cast<unsigned long>(i)); + dataLogF(" #%lu", static_cast<unsigned long>(i)); } - dataLog("\n"); + dataLogF("\n"); } - dataLog("%s Phi Nodes:", prefix); + dataLogF("%s Phi Nodes:", prefix); for (size_t i = 0; i < block->phis.size(); ++i) { NodeIndex phiNodeIndex = block->phis[i]; Node& phiNode = at(phiNodeIndex); if (!phiNode.shouldGenerate() && phiNodeDumpMode == DumpLivePhisOnly) continue; - dataLog(" @%u->(", phiNodeIndex); + dataLogF(" @%u->(", phiNodeIndex); if (phiNode.child1()) { - dataLog("@%u", phiNode.child1().index()); + dataLogF("@%u", phiNode.child1().index()); if (phiNode.child2()) { - dataLog(", @%u", phiNode.child2().index()); + dataLogF(", @%u", phiNode.child2().index()); if (phiNode.child3()) - dataLog(", @%u", phiNode.child3().index()); + dataLogF(", @%u", phiNode.child3().index()); } } - dataLog(")%s", i + 1 < block->phis.size() ? "," : ""); + dataLogF(")%s", i + 1 < block->phis.size() ? "," : ""); } - dataLog("\n"); + dataLogF("\n"); } void Graph::dump() @@ -374,29 +383,29 @@ void Graph::dump() if (!block) continue; dumpBlockHeader("", b, DumpAllPhis); - dataLog(" vars before: "); + dataLogF(" vars before: "); if (block->cfaHasVisited) dumpOperands(block->valuesAtHead, WTF::dataFile()); else - dataLog("<empty>"); - dataLog("\n"); - dataLog(" var links: "); + dataLogF("<empty>"); + dataLogF("\n"); + dataLogF(" var links: "); dumpOperands(block->variablesAtHead, WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); for (size_t i = 0; i < block->size(); ++i) { dumpCodeOrigin("", lastNodeIndex, block->at(i)); dump("", block->at(i)); lastNodeIndex = block->at(i); } - dataLog(" vars after: "); + dataLogF(" vars after: "); if (block->cfaHasVisited) dumpOperands(block->valuesAtTail, WTF::dataFile()); else - dataLog("<empty>"); - dataLog("\n"); - dataLog(" var links: "); + dataLogF("<empty>"); + dataLogF("\n"); + dataLogF(" var links: "); dumpOperands(block->variablesAtTail, WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); } } @@ -451,7 +460,7 @@ void Graph::predictArgumentTypes() at(m_arguments[arg]).variableAccessData()->predict(profile->computeUpdatedPrediction()); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Argument [%zu] prediction: %s\n", arg, speculationToString(at(m_arguments[arg]).variableAccessData()->prediction())); + dataLogF("Argument [%zu] prediction: %s\n", arg, speculationToString(at(m_arguments[arg]).variableAccessData()->prediction())); #endif } } diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h index 9fbb2df07..0c77b2959 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.h +++ b/Source/JavaScriptCore/dfg/DFGGraph.h @@ -220,7 +220,7 @@ public: if (right.hasConstant()) return addImmediateShouldSpeculateInteger(add, left, right); - return Node::shouldSpeculateInteger(left, right) && add.canSpeculateInteger(); + return Node::shouldSpeculateIntegerExpectingDefined(left, right) && add.canSpeculateInteger(); } bool mulShouldSpeculateInteger(Node& mul) @@ -230,18 +230,13 @@ public: Node& left = at(mul.child1()); Node& right = at(mul.child2()); - if (left.hasConstant()) - return mulImmediateShouldSpeculateInteger(mul, right, left); - if (right.hasConstant()) - return mulImmediateShouldSpeculateInteger(mul, left, right); - - return Node::shouldSpeculateInteger(left, right) && mul.canSpeculateInteger() && !nodeMayOverflow(mul.arithNodeFlags()); + return Node::shouldSpeculateIntegerForArithmetic(left, right) && mul.canSpeculateInteger(); } bool negateShouldSpeculateInteger(Node& negate) { ASSERT(negate.op() == ArithNegate); - return at(negate.child1()).shouldSpeculateInteger() && negate.canSpeculateInteger(); + return at(negate.child1()).shouldSpeculateIntegerForArithmetic() && negate.canSpeculateInteger(); } bool addShouldSpeculateInteger(NodeIndex nodeIndex) @@ -493,6 +488,8 @@ public: switch (node.arrayMode().type()) { case Array::Generic: return false; + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: return !node.arrayMode().isOutOfBounds(); @@ -712,7 +709,7 @@ private: if (!immediateValue.isNumber()) return false; - if (!variable.shouldSpeculateInteger()) + if (!variable.shouldSpeculateIntegerExpectingDefined()) return false; if (immediateValue.isInt32()) @@ -734,7 +731,7 @@ private: if (!immediateValue.isInt32()) return false; - if (!variable.shouldSpeculateInteger()) + if (!variable.shouldSpeculateIntegerForArithmetic()) return false; int32_t intImmediate = immediateValue.asInt32(); diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp index c7f941a7a..191aa7fe5 100644 --- a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp +++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp @@ -128,7 +128,7 @@ void JITCompiler::link(LinkBuffer& linkBuffer) { // Link the code, populate data in CodeBlock data structures. #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("JIT code for %p start at [%p, %p). Size = %zu.\n", m_codeBlock, linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize(), linkBuffer.debugSize()); + dataLogF("JIT code for %p start at [%p, %p). Size = %zu.\n", m_codeBlock, linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize(), linkBuffer.debugSize()); #endif // Link all calls out from the JIT code to their respective functions. @@ -188,11 +188,12 @@ void JITCompiler::link(LinkBuffer& linkBuffer) CallLinkInfo& info = m_codeBlock->callLinkInfo(i); info.callType = m_jsCalls[i].m_callType; info.isDFG = true; - info.bytecodeIndex = m_jsCalls[i].m_codeOrigin.bytecodeIndex; + info.codeOrigin = m_jsCalls[i].m_codeOrigin; linkBuffer.link(m_jsCalls[i].m_slowCall, FunctionPtr((m_globalData->getCTIStub(info.callType == CallLinkInfo::Construct ? linkConstructThunkGenerator : linkCallThunkGenerator)).code().executableAddress())); info.callReturnLocation = linkBuffer.locationOfNearCall(m_jsCalls[i].m_slowCall); info.hotPathBegin = linkBuffer.locationOf(m_jsCalls[i].m_targetToCheck); info.hotPathOther = linkBuffer.locationOfNearCall(m_jsCalls[i].m_fastCall); + info.calleeGPR = static_cast<unsigned>(m_jsCalls[i].m_callee); } MacroAssemblerCodeRef osrExitThunk = globalData()->getCTIStub(osrExitGenerationThunkGenerator); diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.h b/Source/JavaScriptCore/dfg/DFGJITCompiler.h index c73934832..0bd88b788 100644 --- a/Source/JavaScriptCore/dfg/DFGJITCompiler.h +++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.h @@ -357,9 +357,9 @@ public: m_propertyAccesses.append(record); } - void addJSCall(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, CodeOrigin codeOrigin) + void addJSCall(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, GPRReg callee, CodeOrigin codeOrigin) { - m_jsCalls.append(JSCallRecord(fastCall, slowCall, targetToCheck, callType, codeOrigin)); + m_jsCalls.append(JSCallRecord(fastCall, slowCall, targetToCheck, callType, callee, codeOrigin)); } void addWeakReference(JSCell* target) @@ -440,11 +440,12 @@ private: Vector<CallExceptionRecord> m_exceptionChecks; struct JSCallRecord { - JSCallRecord(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, CodeOrigin codeOrigin) + JSCallRecord(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, GPRReg callee, CodeOrigin codeOrigin) : m_fastCall(fastCall) , m_slowCall(slowCall) , m_targetToCheck(targetToCheck) , m_callType(callType) + , m_callee(callee) , m_codeOrigin(codeOrigin) { } @@ -453,6 +454,7 @@ private: Call m_slowCall; DataLabelPtr m_targetToCheck; CallLinkInfo::CallType m_callType; + GPRReg m_callee; CodeOrigin m_codeOrigin; }; diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h index e66629ec4..18c8ce16f 100644 --- a/Source/JavaScriptCore/dfg/DFGNode.h +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -62,6 +62,7 @@ struct StructureTransitionData { struct NewArrayBufferData { unsigned startConstant; unsigned numConstants; + IndexingType indexingType; }; // This type used in passing an immediate argument to Node constructor; @@ -268,6 +269,26 @@ struct Node { convertToStructureTransitionWatchpoint(structureSet().singletonStructure()); } + void convertToGetByOffset(unsigned storageAccessDataIndex, NodeIndex storage) + { + ASSERT(m_op == GetById || m_op == GetByIdFlush); + m_opInfo = storageAccessDataIndex; + children.setChild1(Edge(storage)); + m_op = GetByOffset; + m_flags &= ~NodeClobbersWorld; + } + + void convertToPutByOffset(unsigned storageAccessDataIndex, NodeIndex storage) + { + ASSERT(m_op == PutById || m_op == PutByIdDirect); + m_opInfo = storageAccessDataIndex; + children.setChild3(children.child2()); + children.setChild2(children.child1()); + children.setChild1(Edge(storage)); + m_op = PutByOffset; + m_flags &= ~NodeClobbersWorld; + } + JSCell* weakConstant() { ASSERT(op() == WeakJSConstant); @@ -433,6 +454,32 @@ struct Node { return newArrayBufferData()->numConstants; } + bool hasIndexingType() + { + switch (op()) { + case NewArray: + case NewArrayWithSize: + case NewArrayBuffer: + return true; + default: + return false; + } + } + + IndexingType indexingType() + { + ASSERT(hasIndexingType()); + if (op() == NewArrayBuffer) + return newArrayBufferData()->indexingType; + return m_opInfo; + } + + void setIndexingType(IndexingType indexingType) + { + ASSERT(hasIndexingType()); + m_opInfo = indexingType; + } + bool hasRegexpIndex() { return op() == NewRegexp; @@ -518,6 +565,11 @@ struct Node { return (m_flags & NodeResultMask) == NodeResultBoolean; } + bool hasStorageResult() + { + return (m_flags & NodeResultMask) == NodeResultStorage; + } + bool isJump() { return op() == Jump; @@ -649,15 +701,23 @@ struct Node { return mergeSpeculation(m_opInfo2, prediction); } - bool hasFunctionCheckData() + bool hasFunction() { - return op() == CheckFunction; + switch (op()) { + case CheckFunction: + case InheritorIDWatchpoint: + return true; + default: + return false; + } } - JSFunction* function() + JSCell* function() { - ASSERT(hasFunctionCheckData()); - return reinterpret_cast<JSFunction*>(m_opInfo); + ASSERT(hasFunction()); + JSCell* result = reinterpret_cast<JSFunction*>(m_opInfo); + ASSERT(JSValue(result).isFunction()); + return result; } bool hasStructureTransitionData() @@ -702,6 +762,7 @@ struct Node { case StructureTransitionWatchpoint: case ForwardStructureTransitionWatchpoint: case ArrayifyToStructure: + case NewObject: return true; default: return false; @@ -922,16 +983,36 @@ struct Node { return isInt32Speculation(prediction()); } + bool shouldSpeculateIntegerForArithmetic() + { + return isInt32SpeculationForArithmetic(prediction()); + } + + bool shouldSpeculateIntegerExpectingDefined() + { + return isInt32SpeculationExpectingDefined(prediction()); + } + bool shouldSpeculateDouble() { return isDoubleSpeculation(prediction()); } + bool shouldSpeculateDoubleForArithmetic() + { + return isDoubleSpeculationForArithmetic(prediction()); + } + bool shouldSpeculateNumber() { return isNumberSpeculation(prediction()); } + bool shouldSpeculateNumberExpectingDefined() + { + return isNumberSpeculationExpectingDefined(prediction()); + } + bool shouldSpeculateBoolean() { return isBooleanSpeculation(prediction()); @@ -1037,11 +1118,31 @@ struct Node { return op1.shouldSpeculateInteger() && op2.shouldSpeculateInteger(); } + static bool shouldSpeculateIntegerForArithmetic(Node& op1, Node& op2) + { + return op1.shouldSpeculateIntegerForArithmetic() && op2.shouldSpeculateIntegerForArithmetic(); + } + + static bool shouldSpeculateIntegerExpectingDefined(Node& op1, Node& op2) + { + return op1.shouldSpeculateIntegerExpectingDefined() && op2.shouldSpeculateIntegerExpectingDefined(); + } + + static bool shouldSpeculateDoubleForArithmetic(Node& op1, Node& op2) + { + return op1.shouldSpeculateDoubleForArithmetic() && op2.shouldSpeculateDoubleForArithmetic(); + } + static bool shouldSpeculateNumber(Node& op1, Node& op2) { return op1.shouldSpeculateNumber() && op2.shouldSpeculateNumber(); } + static bool shouldSpeculateNumberExpectingDefined(Node& op1, Node& op2) + { + return op1.shouldSpeculateNumberExpectingDefined() && op2.shouldSpeculateNumberExpectingDefined(); + } + static bool shouldSpeculateFinalObject(Node& op1, Node& op2) { return op1.shouldSpeculateFinalObject() && op2.shouldSpeculateFinalObject(); diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp b/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp index 480a7dab9..fb83c5a71 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp +++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp @@ -112,6 +112,12 @@ const char* nodeFlagsAsString(NodeFlags flags) ptr.strcat("PureNum"); hasPrinted = true; } + if (flags & NodeUsedAsOther) { + if (hasPrinted) + ptr.strcat("|"); + ptr.strcat("UseAsOther"); + hasPrinted = true; + } } if (flags & NodeMayOverflow) { diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.h b/Source/JavaScriptCore/dfg/DFGNodeFlags.h index a897d0c4f..5e41bfc6b 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeFlags.h +++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.h @@ -52,14 +52,15 @@ namespace JSC { namespace DFG { #define NodeMayOverflow 0x100 #define NodeMayNegZero 0x200 -#define NodeBackPropMask 0x1C00 +#define NodeBackPropMask 0x3C00 #define NodeUseBottom 0x000 #define NodeUsedAsNumber 0x400 // The result of this computation may be used in a context that observes fractional results. #define NodeNeedsNegZero 0x800 // The result of this computation may be used in a context that observes -0. -#define NodeUsedAsValue (NodeUsedAsNumber | NodeNeedsNegZero) -#define NodeUsedAsInt 0x1000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values. +#define NodeUsedAsOther 0x1000 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined). +#define NodeUsedAsValue (NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther) +#define NodeUsedAsInt 0x2000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values. -#define NodeDoesNotExit 0x2000 // This flag is negated to make it natural for the default to be that a node does exit. +#define NodeDoesNotExit 0x4000 // This flag is negated to make it natural for the default to be that a node does exit. typedef uint16_t NodeFlags; diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h index 624b1ae75..cbcdde660 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeType.h +++ b/Source/JavaScriptCore/dfg/DFGNodeType.h @@ -43,6 +43,11 @@ namespace JSC { namespace DFG { /* code block. */\ macro(WeakJSConstant, NodeResultJS | NodeDoesNotExit) \ \ + /* Marker to indicate that an operation was optimized entirely and all that is left */\ + /* is to make one node alias another. CSE will later usually eliminate this node, */\ + /* though it may choose not to if it would corrupt predictions (very rare). */\ + macro(Identity, NodeResultJS | NodeDoesNotExit) \ + \ /* Nodes for handling functions (both as call and as construct). */\ macro(ConvertThis, NodeResultJS) \ macro(CreateThis, NodeResultJS) /* Note this is not MustGenerate since we're returning it anyway. */ \ @@ -154,6 +159,7 @@ namespace JSC { namespace DFG { macro(GlobalVarWatchpoint, NodeMustGenerate) \ macro(PutGlobalVarCheck, NodeMustGenerate) \ macro(CheckFunction, NodeMustGenerate) \ + macro(InheritorIDWatchpoint, NodeMustGenerate) \ \ /* Optimizations for array mutation. */\ macro(ArrayPush, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \ diff --git a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp index b838c4fb4..ed13ed5b5 100644 --- a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp @@ -44,7 +44,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn ASSERT(!codeBlock->jitCodeMap()); #if ENABLE(JIT_VERBOSE_OSR) - dataLog("OSR in %p(%p) from bc#%u\n", codeBlock, codeBlock->alternative(), bytecodeIndex); + dataLogF("OSR in %p(%p) from bc#%u\n", codeBlock, codeBlock->alternative(), bytecodeIndex); #endif JSGlobalData* globalData = &exec->globalData(); @@ -52,7 +52,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn if (!entry) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR failed because the entrypoint was optimized out.\n"); + dataLogF(" OSR failed because the entrypoint was optimized out.\n"); #endif return 0; } @@ -86,9 +86,9 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn for (size_t argument = 0; argument < entry->m_expectedValues.numberOfArguments(); ++argument) { if (argument >= exec->argumentCountIncludingThis()) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR failed because argument %zu was not passed, expected ", argument); + dataLogF(" OSR failed because argument %zu was not passed, expected ", argument); entry->m_expectedValues.argument(argument).dump(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif return 0; } @@ -101,9 +101,9 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn if (!entry->m_expectedValues.argument(argument).validate(value)) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR failed because argument %zu is %s, expected ", argument, value.description()); + dataLogF(" OSR failed because argument %zu is %s, expected ", argument, value.description()); entry->m_expectedValues.argument(argument).dump(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif return 0; } @@ -113,7 +113,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn if (entry->m_localsForcedDouble.get(local)) { if (!exec->registers()[local].jsValue().isNumber()) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR failed because variable %zu is %s, expected number.\n", local, exec->registers()[local].jsValue().description()); + dataLogF(" OSR failed because variable %zu is %s, expected number.\n", local, exec->registers()[local].jsValue().description()); #endif return 0; } @@ -121,9 +121,9 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn } if (!entry->m_expectedValues.local(local).validate(exec->registers()[local].jsValue())) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR failed because variable %zu is %s, expected ", local, exec->registers()[local].jsValue().description()); + dataLogF(" OSR failed because variable %zu is %s, expected ", local, exec->registers()[local].jsValue().description()); entry->m_expectedValues.local(local).dump(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif return 0; } @@ -138,13 +138,13 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn if (!globalData->interpreter->stack().grow(&exec->registers()[codeBlock->m_numCalleeRegisters])) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR failed because stack growth failed.\n"); + dataLogF(" OSR failed because stack growth failed.\n"); #endif return 0; } #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR should succeed.\n"); + dataLogF(" OSR should succeed.\n"); #endif // 3) Perform data format conversions. @@ -162,7 +162,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn void* result = codeBlock->getJITCode().executableAddressAtOffset(entry->m_machineCodeOffset); #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR returning machine code address %p.\n", result); + dataLogF(" OSR returning machine code address %p.\n", result); #endif return result; diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp index 2ce1c887b..c65443e29 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp @@ -81,7 +81,7 @@ void compileOSRExit(ExecState* exec) recovery = &codeBlock->speculationRecovery(exit.m_recoveryIndex - 1); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Generating OSR exit #%u (seq#%u, bc#%u, @%u, %s) for code block %p.\n", exitIndex, exit.m_streamIndex, exit.m_codeOrigin.bytecodeIndex, exit.m_nodeIndex, exitKindToString(exit.m_kind), codeBlock); + dataLogF("Generating OSR exit #%u (seq#%u, bc#%u, @%u, %s) for code block %p.\n", exitIndex, exit.m_streamIndex, exit.m_codeOrigin.bytecodeIndex, exit.m_nodeIndex, exitKindToString(exit.m_kind), codeBlock); #endif { diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp index df4f3c905..732e67c30 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp @@ -37,14 +37,14 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov { // 1) Pro-forma stuff. #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("OSR exit for Node @%d (", (int)exit.m_nodeIndex); + dataLogF("OSR exit for Node @%d (", (int)exit.m_nodeIndex); for (CodeOrigin codeOrigin = exit.m_codeOrigin; ; codeOrigin = codeOrigin.inlineCallFrame->caller) { - dataLog("bc#%u", codeOrigin.bytecodeIndex); + dataLogF("bc#%u", codeOrigin.bytecodeIndex); if (!codeOrigin.inlineCallFrame) break; - dataLog(" -> %p ", codeOrigin.inlineCallFrame->executable.get()); + dataLogF(" -> %p ", codeOrigin.inlineCallFrame->executable.get()); } - dataLog(") at JIT offset 0x%x ", m_jit.debugOffset()); + dataLogF(") at JIT offset 0x%x ", m_jit.debugOffset()); dumpOperands(operands, WTF::dataFile()); #endif #if DFG_ENABLE(VERBOSE_SPECULATION_FAILURE) @@ -762,7 +762,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov m_jit.jump(GPRInfo::regT2); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog(" -> %p\n", jumpTarget); + dataLogF(" -> %p\n", jumpTarget); #endif } diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp index b278997ab..b83c0b3f5 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp @@ -37,14 +37,14 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov { // 1) Pro-forma stuff. #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("OSR exit for Node @%d (", (int)exit.m_nodeIndex); + dataLogF("OSR exit for Node @%d (", (int)exit.m_nodeIndex); for (CodeOrigin codeOrigin = exit.m_codeOrigin; ; codeOrigin = codeOrigin.inlineCallFrame->caller) { - dataLog("bc#%u", codeOrigin.bytecodeIndex); + dataLogF("bc#%u", codeOrigin.bytecodeIndex); if (!codeOrigin.inlineCallFrame) break; - dataLog(" -> %p ", codeOrigin.inlineCallFrame->executable.get()); + dataLogF(" -> %p ", codeOrigin.inlineCallFrame->executable.get()); } - dataLog(") "); + dataLogF(") "); dumpOperands(operands, WTF::dataFile()); #endif #if DFG_ENABLE(VERBOSE_SPECULATION_FAILURE) @@ -138,7 +138,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov EncodedJSValue* bucket = exit.m_valueProfile.getSpecFailBucket(0); #if DFG_ENABLE(VERBOSE_SPECULATION_FAILURE) - dataLog(" (have exit profile, bucket %p) ", bucket); + dataLogF(" (have exit profile, bucket %p) ", bucket); #endif if (exit.m_jsValueSource.isAddress()) { @@ -243,24 +243,24 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov } #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog(" "); + dataLogF(" "); if (numberOfPoisonedVirtualRegisters) - dataLog("Poisoned=%u ", numberOfPoisonedVirtualRegisters); + dataLogF("Poisoned=%u ", numberOfPoisonedVirtualRegisters); if (numberOfDisplacedVirtualRegisters) - dataLog("Displaced=%u ", numberOfDisplacedVirtualRegisters); + dataLogF("Displaced=%u ", numberOfDisplacedVirtualRegisters); if (haveUnboxedInt32s) - dataLog("UnboxedInt32 "); + dataLogF("UnboxedInt32 "); if (haveUnboxedDoubles) - dataLog("UnboxedDoubles "); + dataLogF("UnboxedDoubles "); if (haveUInt32s) - dataLog("UInt32 "); + dataLogF("UInt32 "); if (haveFPRs) - dataLog("FPR "); + dataLogF("FPR "); if (haveConstants) - dataLog("Constants "); + dataLogF("Constants "); if (haveUndefined) - dataLog("Undefined "); - dataLog(" "); + dataLogF("Undefined "); + dataLogF(" "); #endif ScratchBuffer* scratchBuffer = m_jit.globalData()->scratchBufferForSize(sizeof(EncodedJSValue) * std::max(haveUInt32s ? 2u : 0u, numberOfPoisonedVirtualRegisters + (numberOfDisplacedVirtualRegisters <= GPRInfo::numberOfRegisters ? 0 : numberOfDisplacedVirtualRegisters))); @@ -710,7 +710,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov m_jit.jump(GPRInfo::regT1); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("-> %p\n", jumpTarget); + dataLogF("-> %p\n", jumpTarget); #endif } diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index 0e45e230c..909c657a1 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -27,9 +27,9 @@ #include "DFGOperations.h" #include "Arguments.h" -#include "ButterflyInlineMethods.h" +#include "ButterflyInlines.h" #include "CodeBlock.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "DFGOSRExit.h" #include "DFGRepatch.h" #include "DFGThunks.h" @@ -302,12 +302,12 @@ JSCell* DFG_OPERATION operationCreateThis(ExecState* exec, JSCell* constructor) return constructEmptyObject(exec, jsCast<JSFunction*>(constructor)->cachedInheritorID(exec)); } -JSCell* DFG_OPERATION operationNewObject(ExecState* exec) +JSCell* DFG_OPERATION operationNewObject(ExecState* exec, Structure* structure) { JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); - return constructEmptyObject(exec); + return constructEmptyObject(exec, structure); } EncodedJSValue DFG_OPERATION operationValueAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) @@ -580,6 +580,40 @@ void DFG_OPERATION operationPutByValBeyondArrayBoundsNonStrict(ExecState* exec, array, exec, Identifier::from(exec, index), JSValue::decode(encodedValue), slot); } +void DFG_OPERATION operationPutDoubleByValBeyondArrayBoundsStrict(ExecState* exec, JSObject* array, int32_t index, double value) +{ + JSGlobalData* globalData = &exec->globalData(); + NativeCallFrameTracer tracer(globalData, exec); + + JSValue jsValue = JSValue(JSValue::EncodeAsDouble, value); + + if (index >= 0) { + array->putByIndexInline(exec, index, jsValue, true); + return; + } + + PutPropertySlot slot(true); + array->methodTable()->put( + array, exec, Identifier::from(exec, index), jsValue, slot); +} + +void DFG_OPERATION operationPutDoubleByValBeyondArrayBoundsNonStrict(ExecState* exec, JSObject* array, int32_t index, double value) +{ + JSGlobalData* globalData = &exec->globalData(); + NativeCallFrameTracer tracer(globalData, exec); + + JSValue jsValue = JSValue(JSValue::EncodeAsDouble, value); + + if (index >= 0) { + array->putByIndexInline(exec, index, jsValue, false); + return; + } + + PutPropertySlot slot(false); + array->methodTable()->put( + array, exec, Identifier::from(exec, index), jsValue, slot); +} + EncodedJSValue DFG_OPERATION operationArrayPush(ExecState* exec, EncodedJSValue encodedValue, JSArray* array) { JSGlobalData* globalData = &exec->globalData(); @@ -589,6 +623,15 @@ EncodedJSValue DFG_OPERATION operationArrayPush(ExecState* exec, EncodedJSValue return JSValue::encode(jsNumber(array->length())); } +EncodedJSValue DFG_OPERATION operationArrayPushDouble(ExecState* exec, double value, JSArray* array) +{ + JSGlobalData* globalData = &exec->globalData(); + NativeCallFrameTracer tracer(globalData, exec); + + array->push(exec, JSValue(JSValue::EncodeAsDouble, value)); + return JSValue::encode(jsNumber(array->length())); +} + EncodedJSValue DFG_OPERATION operationArrayPop(ExecState* exec, JSArray* array) { JSGlobalData* globalData = &exec->globalData(); @@ -1019,14 +1062,14 @@ char* DFG_OPERATION operationLinkConstruct(ExecState* execCallee) return linkFor(execCallee, CodeForConstruct); } -inline char* virtualFor(ExecState* execCallee, CodeSpecializationKind kind) +inline char* virtualForWithFunction(ExecState* execCallee, CodeSpecializationKind kind, JSCell*& calleeAsFunctionCell) { ExecState* exec = execCallee->callerFrame(); JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); JSValue calleeAsValue = execCallee->calleeAsValue(); - JSCell* calleeAsFunctionCell = getJSFunction(calleeAsValue); + calleeAsFunctionCell = getJSFunction(calleeAsValue); if (UNLIKELY(!calleeAsFunctionCell)) return reinterpret_cast<char*>(handleHostCall(execCallee, calleeAsValue, kind)); @@ -1044,6 +1087,56 @@ inline char* virtualFor(ExecState* execCallee, CodeSpecializationKind kind) return reinterpret_cast<char*>(executable->generatedJITCodeWithArityCheckFor(kind).executableAddress()); } +inline char* virtualFor(ExecState* execCallee, CodeSpecializationKind kind) +{ + JSCell* calleeAsFunctionCellIgnored; + return virtualForWithFunction(execCallee, kind, calleeAsFunctionCellIgnored); +} + +static bool attemptToOptimizeClosureCall(ExecState* execCallee, JSCell* calleeAsFunctionCell, CallLinkInfo& callLinkInfo) +{ + if (!calleeAsFunctionCell) + return false; + + JSFunction* callee = jsCast<JSFunction*>(calleeAsFunctionCell); + JSFunction* oldCallee = callLinkInfo.callee.get(); + + if (!oldCallee + || oldCallee->structure() != callee->structure() + || oldCallee->executable() != callee->executable()) + return false; + + ASSERT(callee->executable()->hasJITCodeForCall()); + MacroAssemblerCodePtr codePtr = callee->executable()->generatedJITCodeForCall().addressForCall(); + + CodeBlock* codeBlock; + if (callee->executable()->isHostFunction()) + codeBlock = 0; + else { + codeBlock = &jsCast<FunctionExecutable*>(callee->executable())->generatedBytecodeForCall(); + if (execCallee->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters())) + return false; + } + + dfgLinkClosureCall( + execCallee, callLinkInfo, codeBlock, + callee->structure(), callee->executable(), codePtr); + + return true; +} + +char* DFG_OPERATION operationLinkClosureCall(ExecState* execCallee) +{ + JSCell* calleeAsFunctionCell; + char* result = virtualForWithFunction(execCallee, CodeForCall, calleeAsFunctionCell); + CallLinkInfo& callLinkInfo = execCallee->callerFrame()->codeBlock()->getCallLinkInfo(execCallee->returnPC()); + + if (!attemptToOptimizeClosureCall(execCallee, calleeAsFunctionCell, callLinkInfo)) + dfgLinkSlowFor(execCallee, callLinkInfo, CodeForCall); + + return result; +} + char* DFG_OPERATION operationVirtualCall(ExecState* execCallee) { return virtualFor(execCallee, CodeForCall); @@ -1327,30 +1420,36 @@ char* DFG_OPERATION operationReallocateButterflyToGrowPropertyStorage(ExecState* return reinterpret_cast<char*>(result); } -char* DFG_OPERATION operationEnsureContiguous(ExecState* exec, JSObject* object) +char* DFG_OPERATION operationEnsureInt32(ExecState* exec, JSObject* object) { JSGlobalData& globalData = exec->globalData(); NativeCallFrameTracer tracer(&globalData, exec); - return reinterpret_cast<char*>(object->ensureContiguous(globalData)); + return reinterpret_cast<char*>(object->ensureInt32(globalData)); } -char* DFG_OPERATION operationEnsureArrayStorage(ExecState* exec, JSObject* object) +char* DFG_OPERATION operationEnsureDouble(ExecState* exec, JSObject* object) { JSGlobalData& globalData = exec->globalData(); NativeCallFrameTracer tracer(&globalData, exec); + + return reinterpret_cast<char*>(object->ensureDouble(globalData)); +} - return reinterpret_cast<char*>(object->ensureArrayStorage(globalData)); +char* DFG_OPERATION operationEnsureContiguous(ExecState* exec, JSObject* object) +{ + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); + + return reinterpret_cast<char*>(object->ensureContiguous(globalData)); } -char* DFG_OPERATION operationEnsureContiguousOrArrayStorage(ExecState* exec, JSObject* object, int32_t index) +char* DFG_OPERATION operationEnsureArrayStorage(ExecState* exec, JSObject* object) { JSGlobalData& globalData = exec->globalData(); NativeCallFrameTracer tracer(&globalData, exec); - if (static_cast<unsigned>(index) >= MIN_SPARSE_ARRAY_INDEX) - return reinterpret_cast<char*>(object->ensureArrayStorage(globalData)); - return reinterpret_cast<char*>(object->ensureIndexedStorage(globalData)); + return reinterpret_cast<char*>(object->ensureArrayStorage(globalData)); } double DFG_OPERATION operationFModOnInts(int32_t a, int32_t b) @@ -1423,7 +1522,7 @@ void DFG_OPERATION debugOperationPrintSpeculationFailure(ExecState* exec, void* SpeculationFailureDebugInfo* debugInfo = static_cast<SpeculationFailureDebugInfo*>(debugInfoRaw); CodeBlock* codeBlock = debugInfo->codeBlock; CodeBlock* alternative = codeBlock->alternative(); - dataLog("Speculation failure in %p at @%u with executeCounter = %s, " + dataLogF("Speculation failure in %p at @%u with executeCounter = %s, " "reoptimizationRetryCounter = %u, optimizationDelayCounter = %u, " "osrExitCounter = %u\n", codeBlock, @@ -1438,7 +1537,7 @@ void DFG_OPERATION debugOperationPrintSpeculationFailure(ExecState* exec, void* extern "C" void DFG_OPERATION triggerReoptimizationNow(CodeBlock* codeBlock) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog("%p: Entered reoptimize\n", codeBlock); + dataLogF("%p: Entered reoptimize\n", codeBlock); #endif // We must be called with the baseline code block. ASSERT(JITCode::isBaselineCode(codeBlock->getJITType())); diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h index 8d2beacec..00e6b07b7 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.h +++ b/Source/JavaScriptCore/dfg/DFGOperations.h @@ -64,6 +64,7 @@ typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EAZ)(ExecState*, JSArray*, typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECI)(ExecState*, JSCell*, Identifier*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECJ)(ExecState*, JSCell*, EncodedJSValue); +typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EDA)(ExecState*, double, JSArray*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EGriJsgI)(ExecState*, ResolveOperation*, JSGlobalObject*, Identifier*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EI)(ExecState*, Identifier*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EIRo)(ExecState*, Identifier*, ResolveOperations*); @@ -84,6 +85,7 @@ typedef JSCell* DFG_OPERATION (*C_DFGOperation_E)(ExecState*); typedef JSCell* DFG_OPERATION (*C_DFGOperation_EC)(ExecState*, JSCell*); typedef JSCell* DFG_OPERATION (*C_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*); typedef JSCell* DFG_OPERATION (*C_DFGOperation_EIcf)(ExecState*, InlineCallFrame*); +typedef JSCell* DFG_OPERATION (*C_DFGOperation_ESt)(ExecState*, Structure*); typedef double DFG_OPERATION (*D_DFGOperation_DD)(double, double); typedef double DFG_OPERATION (*D_DFGOperation_ZZ)(int32_t, int32_t); typedef double DFG_OPERATION (*D_DFGOperation_EJ)(ExecState*, EncodedJSValue); @@ -92,6 +94,7 @@ typedef size_t DFG_OPERATION (*S_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*) typedef size_t DFG_OPERATION (*S_DFGOperation_EJ)(ExecState*, EncodedJSValue); typedef size_t DFG_OPERATION (*S_DFGOperation_EJJ)(ExecState*, EncodedJSValue, EncodedJSValue); typedef size_t DFG_OPERATION (*S_DFGOperation_J)(EncodedJSValue); +typedef void DFG_OPERATION (*V_DFGOperation_EOZD)(ExecState*, JSObject*, int32_t, double); typedef void DFG_OPERATION (*V_DFGOperation_EOZJ)(ExecState*, JSObject*, int32_t, EncodedJSValue); typedef void DFG_OPERATION (*V_DFGOperation_EC)(ExecState*, JSCell*); typedef void DFG_OPERATION (*V_DFGOperation_ECIcf)(ExecState*, JSCell*, InlineCallFrame*); @@ -116,7 +119,7 @@ typedef char* DFG_OPERATION (*P_DFGOperation_EStSS)(ExecState*, Structure*, size typedef char* DFG_OPERATION (*P_DFGOperation_EStZ)(ExecState*, Structure*, int32_t); // These routines are provide callbacks out to C++ implementations of operations too complex to JIT. -JSCell* DFG_OPERATION operationNewObject(ExecState*) WTF_INTERNAL; +JSCell* DFG_OPERATION operationNewObject(ExecState*, Structure*) WTF_INTERNAL; JSCell* DFG_OPERATION operationCreateThis(ExecState*, JSCell* constructor) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationConvertThis(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationValueAdd(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL; @@ -148,7 +151,10 @@ void DFG_OPERATION operationPutByValCellStrict(ExecState*, JSCell*, EncodedJSVal void DFG_OPERATION operationPutByValCellNonStrict(ExecState*, JSCell*, EncodedJSValue encodedProperty, EncodedJSValue encodedValue) WTF_INTERNAL; void DFG_OPERATION operationPutByValBeyondArrayBoundsStrict(ExecState*, JSObject*, int32_t index, EncodedJSValue encodedValue) WTF_INTERNAL; void DFG_OPERATION operationPutByValBeyondArrayBoundsNonStrict(ExecState*, JSObject*, int32_t index, EncodedJSValue encodedValue) WTF_INTERNAL; +void DFG_OPERATION operationPutDoubleByValBeyondArrayBoundsStrict(ExecState*, JSObject*, int32_t index, double value) WTF_INTERNAL; +void DFG_OPERATION operationPutDoubleByValBeyondArrayBoundsNonStrict(ExecState*, JSObject*, int32_t index, double value) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationArrayPush(ExecState*, EncodedJSValue encodedValue, JSArray*) WTF_INTERNAL; +EncodedJSValue DFG_OPERATION operationArrayPushDouble(ExecState*, double value, JSArray*) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationArrayPop(ExecState*, JSArray*) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationArrayPopAndRecoverLength(ExecState*, JSArray*) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationRegExpExec(ExecState*, JSCell*, JSCell*) WTF_INTERNAL; @@ -175,6 +181,7 @@ size_t DFG_OPERATION operationCompareStrictEqCell(ExecState*, EncodedJSValue enc size_t DFG_OPERATION operationCompareStrictEq(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL; char* DFG_OPERATION operationVirtualCall(ExecState*) WTF_INTERNAL; char* DFG_OPERATION operationLinkCall(ExecState*) WTF_INTERNAL; +char* DFG_OPERATION operationLinkClosureCall(ExecState*) WTF_INTERNAL; char* DFG_OPERATION operationVirtualConstruct(ExecState*) WTF_INTERNAL; char* DFG_OPERATION operationLinkConstruct(ExecState*) WTF_INTERNAL; JSCell* DFG_OPERATION operationCreateActivation(ExecState*) WTF_INTERNAL; @@ -195,9 +202,10 @@ 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 operationEnsureInt32(ExecState*, JSObject*); +char* DFG_OPERATION operationEnsureDouble(ExecState*, JSObject*); char* DFG_OPERATION operationEnsureContiguous(ExecState*, JSObject*); char* DFG_OPERATION operationEnsureArrayStorage(ExecState*, JSObject*); -char* DFG_OPERATION operationEnsureContiguousOrArrayStorage(ExecState*, JSObject*, int32_t); // 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/DFGPhase.cpp b/Source/JavaScriptCore/dfg/DFGPhase.cpp index f97c49e31..20301e814 100644 --- a/Source/JavaScriptCore/dfg/DFGPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPhase.cpp @@ -35,8 +35,8 @@ namespace JSC { namespace DFG { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) void Phase::beginPhase() { - dataLog("Beginning DFG phase %s.\n", m_name); - dataLog("Graph before %s:\n", m_name); + dataLogF("Beginning DFG phase %s.\n", m_name); + dataLogF("Graph before %s:\n", m_name); m_graph.dump(); } #endif diff --git a/Source/JavaScriptCore/dfg/DFGPhase.h b/Source/JavaScriptCore/dfg/DFGPhase.h index a73d26baf..939e199e0 100644 --- a/Source/JavaScriptCore/dfg/DFGPhase.h +++ b/Source/JavaScriptCore/dfg/DFGPhase.h @@ -83,7 +83,7 @@ bool runAndLog(PhaseType& phase) bool result = phase.run(); #if DFG_ENABLE(DEBUG_VERBOSE) if (result) - dataLog("Phase %s changed the IR.\n", phase.name()); + dataLogF("Phase %s changed the IR.\n", phase.name()); #endif return result; } diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index 3e8ead5c6..4b8a17285 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -116,6 +116,52 @@ private: return false; return !!m_graph.valueOfNumberConstant(nodeIndex); } + + bool isWithinPowerOfTwoForConstant(Node& node, int power) + { + JSValue immediateValue = node.valueOfJSConstant(codeBlock()); + if (!immediateValue.isInt32()) + return false; + int32_t intImmediate = immediateValue.asInt32(); + return intImmediate > -(1 << power) && intImmediate < (1 << power); + } + + bool isWithinPowerOfTwoNonRecursive(NodeIndex nodeIndex, int power) + { + Node& node = m_graph[nodeIndex]; + if (node.op() != JSConstant) + return false; + return isWithinPowerOfTwoForConstant(node, power); + } + + bool isWithinPowerOfTwo(NodeIndex nodeIndex, int power) + { + Node& node = m_graph[nodeIndex]; + switch (node.op()) { + case JSConstant: { + return isWithinPowerOfTwoForConstant(node, power); + } + + case BitAnd: { + return isWithinPowerOfTwoNonRecursive(node.child1().index(), power) + || isWithinPowerOfTwoNonRecursive(node.child2().index(), power); + } + + case BitRShift: + case BitURShift: { + Node& shiftAmount = m_graph[node.child2()]; + if (shiftAmount.op() != JSConstant) + return false; + JSValue immediateValue = shiftAmount.valueOfJSConstant(codeBlock()); + if (!immediateValue.isInt32()) + return false; + return immediateValue > 32 - power; + } + + default: + return false; + } + } SpeculatedType speculatedDoubleTypeForPrediction(SpeculatedType value) { @@ -140,7 +186,7 @@ private: NodeFlags flags = node.flags() & NodeBackPropMask; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" %s @%u: %s ", Graph::opName(op), m_compileIndex, nodeFlagsAsString(flags)); + dataLogF(" %s @%u: %s ", Graph::opName(op), m_compileIndex, nodeFlagsAsString(flags)); #endif bool changed = false; @@ -184,7 +230,7 @@ private: case BitURShift: { changed |= setPrediction(SpecInt32); flags |= NodeUsedAsInt; - flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero); + flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther); changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; @@ -193,7 +239,7 @@ private: case ValueToInt32: { changed |= setPrediction(SpecInt32); flags |= NodeUsedAsInt; - flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero); + flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther); changed |= m_graph[node.child1()].mergeFlags(flags); break; } @@ -221,28 +267,10 @@ private: case StringCharCodeAt: { changed |= mergePrediction(SpecInt32); changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); - changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); + changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt); break; } - case ArithMod: { - SpeculatedType left = m_graph[node.child1()].prediction(); - SpeculatedType right = m_graph[node.child2()].prediction(); - - if (left && right) { - if (isInt32Speculation(mergeSpeculations(left, right)) - && nodeCanSpeculateInteger(node.arithNodeFlags())) - changed |= mergePrediction(SpecInt32); - else - changed |= mergePrediction(SpecDouble); - } - - flags |= NodeUsedAsValue; - changed |= m_graph[node.child1()].mergeFlags(flags); - changed |= m_graph[node.child2()].mergeFlags(flags); - break; - } - case UInt32ToNumber: { if (nodeCanSpeculateInteger(node.arithNodeFlags())) changed |= mergePrediction(SpecInt32); @@ -258,7 +286,7 @@ private: SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { - if (isNumberSpeculation(left) && isNumberSpeculation(right)) { + if (isNumberSpeculationExpectingDefined(left) && isNumberSpeculationExpectingDefined(right)) { if (m_graph.addShouldSpeculateInteger(node)) changed |= mergePrediction(SpecInt32); else @@ -272,6 +300,8 @@ private: if (isNotNegZero(node.child1().index()) || isNotNegZero(node.child2().index())) flags &= ~NodeNeedsNegZero; + if (m_graph[node.child1()].hasNumberResult() || m_graph[node.child2()].hasNumberResult()) + flags &= ~NodeUsedAsOther; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); @@ -291,6 +321,7 @@ private: if (isNotNegZero(node.child1().index()) || isNotNegZero(node.child2().index())) flags &= ~NodeNeedsNegZero; + flags &= ~NodeUsedAsOther; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); @@ -310,6 +341,7 @@ private: if (isNotZero(node.child1().index()) || isNotZero(node.child2().index())) flags &= ~NodeNeedsNegZero; + flags &= ~NodeUsedAsOther; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); @@ -324,6 +356,8 @@ private: changed |= mergePrediction(speculatedDoubleTypeForPrediction(m_graph[node.child1()].prediction())); } + flags &= ~NodeUsedAsOther; + changed |= m_graph[node.child1()].mergeFlags(flags); break; @@ -333,7 +367,7 @@ private: SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { - if (isInt32Speculation(mergeSpeculations(left, right)) + if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()]) && nodeCanSpeculateInteger(node.arithNodeFlags())) changed |= mergePrediction(SpecInt32); else @@ -341,6 +375,8 @@ private: } flags |= NodeUsedAsNumber; + flags &= ~NodeUsedAsOther; + changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; @@ -359,10 +395,20 @@ private: // As soon as a multiply happens, we can easily end up in the part // of the double domain where the point at which you do truncation - // can change the outcome. So, ArithMul always checks for overflow - // no matter what, and always forces its inputs to check as well. + // can change the outcome. So, ArithMul always forces its inputs to + // check for overflow. Additionally, it will have to check for overflow + // itself unless we can prove that there is no way for the values + // produced to cause double rounding. + + if (!isWithinPowerOfTwo(node.child1().index(), 22) + && !isWithinPowerOfTwo(node.child2().index(), 22)) + flags |= NodeUsedAsNumber; + + changed |= node.mergeFlags(flags); flags |= NodeUsedAsNumber | NodeNeedsNegZero; + flags &= ~NodeUsedAsOther; + changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; @@ -373,7 +419,7 @@ private: SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { - if (isInt32Speculation(mergeSpeculations(left, right)) + if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()]) && nodeCanSpeculateInteger(node.arithNodeFlags())) changed |= mergePrediction(SpecInt32); else @@ -382,10 +428,32 @@ private: // As soon as a multiply happens, we can easily end up in the part // of the double domain where the point at which you do truncation - // can change the outcome. So, ArithMul always checks for overflow + // can change the outcome. So, ArithDiv always checks for overflow // no matter what, and always forces its inputs to check as well. flags |= NodeUsedAsNumber | NodeNeedsNegZero; + flags &= ~NodeUsedAsOther; + + changed |= m_graph[node.child1()].mergeFlags(flags); + changed |= m_graph[node.child2()].mergeFlags(flags); + break; + } + + case ArithMod: { + SpeculatedType left = m_graph[node.child1()].prediction(); + SpeculatedType right = m_graph[node.child2()].prediction(); + + if (left && right) { + if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()]) + && nodeCanSpeculateInteger(node.arithNodeFlags())) + changed |= mergePrediction(SpecInt32); + else + changed |= mergePrediction(SpecDouble); + } + + flags |= NodeUsedAsNumber | NodeNeedsNegZero; + flags &= ~NodeUsedAsOther; + changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; @@ -393,18 +461,20 @@ private: case ArithSqrt: { changed |= setPrediction(SpecDouble); - changed |= m_graph[node.child1()].mergeFlags(flags | NodeUsedAsValue); + flags |= NodeUsedAsNumber | NodeNeedsNegZero; + flags &= ~NodeUsedAsOther; + changed |= m_graph[node.child1()].mergeFlags(flags); break; } case ArithAbs: { SpeculatedType child = m_graph[node.child1()].prediction(); - if (nodeCanSpeculateInteger(node.arithNodeFlags())) - changed |= mergePrediction(child); + if (isInt32SpeculationForArithmetic(child) + && nodeCanSpeculateInteger(node.arithNodeFlags())) + changed |= mergePrediction(SpecInt32); else - changed |= setPrediction(speculatedDoubleTypeForPrediction(child)); + changed |= mergePrediction(speculatedDoubleTypeForPrediction(child)); - flags &= ~NodeNeedsNegZero; changed |= m_graph[node.child1()].mergeFlags(flags); break; } @@ -447,13 +517,13 @@ private: changed |= mergePrediction(node.getHeapPrediction()); changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); - changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); + changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt); break; } case GetMyArgumentByValSafe: { changed |= mergePrediction(node.getHeapPrediction()); - changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); + changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt); break; } @@ -554,7 +624,7 @@ private: case NewArrayWithSize: { changed |= setPrediction(SpecArray); - changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); + changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue | NodeUsedAsInt); break; } @@ -571,7 +641,7 @@ private: case StringCharAt: { changed |= setPrediction(SpecString); changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); - changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); + changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt); break; } @@ -580,7 +650,7 @@ private: for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); ++childIdx) - changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsNumber); + changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther); break; } @@ -636,16 +706,17 @@ private: case PhantomArguments: case CheckArray: case Arrayify: - case ArrayifyToStructure: { + case ArrayifyToStructure: + case Identity: { // This node should never be visible at this stage of compilation. It is // inserted by fixup(), which follows this phase. - ASSERT_NOT_REACHED(); + CRASH(); break; } case PutByVal: changed |= m_graph[m_graph.varArgChild(node, 0)].mergeFlags(NodeUsedAsValue); - changed |= m_graph[m_graph.varArgChild(node, 1)].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); + changed |= m_graph[m_graph.varArgChild(node, 1)].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt); changed |= m_graph[m_graph.varArgChild(node, 2)].mergeFlags(NodeUsedAsValue); break; @@ -690,6 +761,7 @@ private: case CheckArgumentsNotCreated: case GlobalVarWatchpoint: case GarbageValue: + case InheritorIDWatchpoint: changed |= mergeDefaultFlags(node); break; @@ -700,7 +772,7 @@ private: break; case LastNodeType: - ASSERT_NOT_REACHED(); + CRASH(); break; #else default: @@ -710,7 +782,7 @@ private: } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("%s\n", speculationToString(m_graph[m_compileIndex].prediction())); + dataLogF("%s\n", speculationToString(m_graph[m_compileIndex].prediction())); #endif m_changed |= changed; @@ -743,7 +815,7 @@ private: void propagateForward() { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Propagating predictions forward [%u]\n", ++m_count); + dataLogF("Propagating predictions forward [%u]\n", ++m_count); #endif for (m_compileIndex = 0; m_compileIndex < m_graph.size(); ++m_compileIndex) propagate(m_graph[m_compileIndex]); @@ -752,7 +824,7 @@ private: void propagateBackward() { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Propagating predictions backward [%u]\n", ++m_count); + dataLogF("Propagating predictions backward [%u]\n", ++m_count); #endif for (m_compileIndex = m_graph.size(); m_compileIndex-- > 0;) propagate(m_graph[m_compileIndex]); @@ -761,7 +833,7 @@ private: void doRoundOfDoubleVoting() { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Voting on double uses of locals [%u]\n", m_count); + dataLogF("Voting on double uses of locals [%u]\n", m_count); #endif for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) m_graph.m_variableAccessData[i].find()->clearVotes(); @@ -776,7 +848,7 @@ private: DoubleBallot ballot; - if (isNumberSpeculation(left) && isNumberSpeculation(right) + if (isNumberSpeculationExpectingDefined(left) && isNumberSpeculationExpectingDefined(right) && !m_graph.addShouldSpeculateInteger(node)) ballot = VoteDouble; else @@ -814,7 +886,7 @@ private: DoubleBallot ballot; if (isNumberSpeculation(left) && isNumberSpeculation(right) - && !(Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child1()]) + && !(Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child1()]) && node.canSpeculateInteger())) ballot = VoteDouble; else @@ -827,7 +899,7 @@ private: case ArithAbs: DoubleBallot ballot; - if (!(m_graph[node.child1()].shouldSpeculateInteger() + if (!(m_graph[node.child1()].shouldSpeculateIntegerForArithmetic() && node.canSpeculateInteger())) ballot = VoteDouble; else @@ -849,6 +921,24 @@ private: break; } + case PutByVal: + case PutByValAlias: { + Edge child1 = m_graph.varArgChild(node, 0); + Edge child2 = m_graph.varArgChild(node, 1); + Edge child3 = m_graph.varArgChild(node, 2); + m_graph.vote(child1, VoteValue); + m_graph.vote(child2, VoteValue); + switch (node.arrayMode().type()) { + case Array::Double: + m_graph.vote(child3, VoteDouble); + break; + default: + m_graph.vote(child3, VoteValue); + break; + } + break; + } + default: m_graph.vote(node, VoteValue); break; diff --git a/Source/JavaScriptCore/dfg/DFGRegisterBank.h b/Source/JavaScriptCore/dfg/DFGRegisterBank.h index 1d1d6fa52..3dbd1fe91 100644 --- a/Source/JavaScriptCore/dfg/DFGRegisterBank.h +++ b/Source/JavaScriptCore/dfg/DFGRegisterBank.h @@ -75,7 +75,6 @@ class RegisterBank { public: RegisterBank() - : m_lastAllocated(NUM_REGS - 1) { } @@ -86,12 +85,7 @@ public: { VirtualRegister ignored; - for (uint32_t i = m_lastAllocated + 1; i < NUM_REGS; ++i) { - if (!m_data[i].lockCount && m_data[i].name == InvalidVirtualRegister) - return allocateInternal(i, ignored); - } - // Loop over the remaining entries. - for (uint32_t i = 0; i <= m_lastAllocated; ++i) { + for (uint32_t i = 0; i < NUM_REGS; ++i) { if (!m_data[i].lockCount && m_data[i].name == InvalidVirtualRegister) return allocateInternal(i, ignored); } @@ -115,9 +109,6 @@ public: uint32_t currentLowest = NUM_REGS; SpillHint currentSpillOrder = SpillHintInvalid; - // Scan through all register, starting at the last allocated & looping around. - ASSERT(m_lastAllocated < NUM_REGS); - // This loop is broken into two halves, looping from the last allocated // register (the register returned last time this method was called) to // the maximum register value, then from 0 to the last allocated. @@ -125,7 +116,7 @@ public: // thrash, and minimize time spent scanning locked registers in allocation. // If a unlocked and unnamed register is found return it immediately. // Otherwise, find the first unlocked register with the lowest spillOrder. - for (uint32_t i = m_lastAllocated + 1; i < NUM_REGS; ++i) { + for (uint32_t i = 0 ; i < NUM_REGS; ++i) { // (1) If the current register is locked, it is not a candidate. if (m_data[i].lockCount) continue; @@ -140,18 +131,6 @@ public: currentLowest = i; } } - // Loop over the remaining entries. - for (uint32_t i = 0; i <= m_lastAllocated; ++i) { - if (m_data[i].lockCount) - continue; - SpillHint spillOrder = m_data[i].spillOrder; - if (spillOrder == SpillHintInvalid) - return allocateInternal(i, spillMe); - if (spillOrder < currentSpillOrder) { - currentSpillOrder = spillOrder; - currentLowest = i; - } - } // Deadlock check - this could only occur is all registers are locked! ASSERT(currentLowest != NUM_REGS && currentSpillOrder != SpillHintInvalid); @@ -237,11 +216,11 @@ public: // For each register, print the VirtualRegister 'name'. for (uint32_t i =0; i < NUM_REGS; ++i) { if (m_data[i].name != InvalidVirtualRegister) - dataLog("[%02d]", m_data[i].name); + dataLogF("[%02d]", m_data[i].name); else - dataLog("[--]"); + dataLogF("[--]"); } - dataLog("\n"); + dataLogF("\n"); } #endif @@ -354,7 +333,6 @@ private: // Mark the register as locked (with a lock count of 1). m_data[i].lockCount = 1; - m_lastAllocated = i; return BankInfo::toRegister(i); } @@ -378,8 +356,6 @@ private: // Holds the current status of all registers. MapEntry m_data[NUM_REGS]; - // Used to to implement a simple round-robin like allocation scheme. - uint32_t m_lastAllocated; }; } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGRepatch.cpp b/Source/JavaScriptCore/dfg/DFGRepatch.cpp index 7c15ef33e..a20eb544a 100644 --- a/Source/JavaScriptCore/dfg/DFGRepatch.cpp +++ b/Source/JavaScriptCore/dfg/DFGRepatch.cpp @@ -114,6 +114,23 @@ static void addStructureTransitionCheck( failureCases, scratchGPR); } +static void replaceWithJump(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo, const MacroAssemblerCodePtr target) +{ + if (MacroAssembler::canJumpReplacePatchableBranchPtrWithPatch()) { + repatchBuffer.replaceWithJump( + RepatchBuffer::startOfPatchableBranchPtrWithPatchOnAddress( + stubInfo.callReturnLocation.dataLabelPtrAtOffset( + -(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall)), + CodeLocationLabel(target)); + return; + } + + repatchBuffer.relink( + stubInfo.callReturnLocation.jumpAtOffset( + stubInfo.patch.dfg.deltaCallToStructCheck), + CodeLocationLabel(target)); +} + static void emitRestoreScratch(MacroAssembler& stubJit, bool needToRestoreScratch, GPRReg scratchGPR, MacroAssembler::Jump& success, MacroAssembler::Jump& fail, MacroAssembler::JumpList failureCases) { if (needToRestoreScratch) { @@ -284,7 +301,7 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier stubInfo.patch.dfg.deltaCallToDone).executableAddress())); RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine->code().code())); + replaceWithJump(repatchBuffer, stubInfo, stubInfo.stubRoutine->code().code()); repatchBuffer.relink(stubInfo.callReturnLocation, operationGetById); return true; @@ -334,7 +351,7 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier generateProtoChainAccessStub(exec, stubInfo, prototypeChain, count, offset, structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToSlowCase), stubInfo.stubRoutine); RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine->code().code())); + replaceWithJump(repatchBuffer, stubInfo, stubInfo.stubRoutine->code().code()); repatchBuffer.relink(stubInfo.callReturnLocation, operationGetByIdProtoBuildList); stubInfo.initGetByIdChain(*globalData, codeBlock->ownerExecutable(), structure, prototypeChain, count, true); @@ -518,9 +535,11 @@ static bool tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identi polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), stubRoutine, structure, isDirect); - CodeLocationJump jumpLocation = stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck); RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code())); + repatchBuffer.relink( + stubInfo.callReturnLocation.jumpAtOffset( + stubInfo.patch.dfg.deltaCallToStructCheck), + CodeLocationLabel(stubRoutine->code().code())); if (listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1)) return true; @@ -584,9 +603,8 @@ static bool tryBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const I polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), stubRoutine, structure, true); - CodeLocationJump jumpLocation = stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck); RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code())); + replaceWithJump(repatchBuffer, stubInfo, stubRoutine->code().code()); if (listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1)) return true; @@ -976,7 +994,10 @@ static bool tryCachePutByID(ExecState* exec, JSValue baseValue, const Identifier stubInfo.stubRoutine); RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine->code().code())); + repatchBuffer.relink( + stubInfo.callReturnLocation.jumpAtOffset( + stubInfo.patch.dfg.deltaCallToStructCheck), + CodeLocationLabel(stubInfo.stubRoutine->code().code())); repatchBuffer.relink(stubInfo.callReturnLocation, appropriateListBuildingPutByIdFunction(slot, putKind)); stubInfo.initPutByIdTransition(*globalData, codeBlock->ownerExecutable(), oldStructure, structure, prototypeChain, putKind == Direct); @@ -1092,8 +1113,20 @@ void dfgBuildPutByIdList(ExecState* exec, JSValue baseValue, const Identifier& p dfgRepatchCall(exec->codeBlock(), stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind)); } +static void linkSlowFor(RepatchBuffer& repatchBuffer, JSGlobalData* globalData, CallLinkInfo& callLinkInfo, CodeSpecializationKind kind) +{ + if (kind == CodeForCall) { + repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(virtualCallThunkGenerator).code()); + return; + } + ASSERT(kind == CodeForConstruct); + repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(virtualConstructThunkGenerator).code()); +} + void dfgLinkFor(ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCodeBlock, JSFunction* callee, MacroAssemblerCodePtr codePtr, CodeSpecializationKind kind) { + ASSERT(!callLinkInfo.stub); + CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock(); JSGlobalData* globalData = callerCodeBlock->globalData(); @@ -1108,17 +1141,125 @@ void dfgLinkFor(ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCo calleeCodeBlock->linkIncomingCall(&callLinkInfo); if (kind == CodeForCall) { - repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(virtualCallThunkGenerator).code()); + repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(linkClosureCallThunkGenerator).code()); return; } + ASSERT(kind == CodeForConstruct); - repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(virtualConstructThunkGenerator).code()); + linkSlowFor(repatchBuffer, globalData, callLinkInfo, CodeForConstruct); +} + +void dfgLinkSlowFor(ExecState* exec, CallLinkInfo& callLinkInfo, CodeSpecializationKind kind) +{ + CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock(); + JSGlobalData* globalData = callerCodeBlock->globalData(); + + RepatchBuffer repatchBuffer(callerCodeBlock); + + linkSlowFor(repatchBuffer, globalData, callLinkInfo, kind); +} + +void dfgLinkClosureCall(ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCodeBlock, Structure* structure, ExecutableBase* executable, MacroAssemblerCodePtr codePtr) +{ + ASSERT(!callLinkInfo.stub); + + CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock(); + JSGlobalData* globalData = callerCodeBlock->globalData(); + + GPRReg calleeGPR = static_cast<GPRReg>(callLinkInfo.calleeGPR); + + CCallHelpers stubJit(globalData, callerCodeBlock); + + CCallHelpers::JumpList slowPath; + +#if USE(JSVALUE64) + slowPath.append( + stubJit.branchTest64( + CCallHelpers::NonZero, calleeGPR, GPRInfo::tagMaskRegister)); +#else + // We would have already checked that the callee is a cell. +#endif + + slowPath.append( + stubJit.branchPtr( + CCallHelpers::NotEqual, + CCallHelpers::Address(calleeGPR, JSCell::structureOffset()), + CCallHelpers::TrustedImmPtr(structure))); + + slowPath.append( + stubJit.branchPtr( + CCallHelpers::NotEqual, + CCallHelpers::Address(calleeGPR, JSFunction::offsetOfExecutable()), + CCallHelpers::TrustedImmPtr(executable))); + + stubJit.loadPtr( + CCallHelpers::Address(calleeGPR, JSFunction::offsetOfScopeChain()), + GPRInfo::returnValueGPR); + +#if USE(JSVALUE64) + stubJit.store64( + GPRInfo::returnValueGPR, + CCallHelpers::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain))); +#else + stubJit.storePtr( + GPRInfo::returnValueGPR, + CCallHelpers::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); + stubJit.store32( + CCallHelpers::TrustedImm32(JSValue::CellTag), + CCallHelpers::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); +#endif + + JITCompiler::Call call = stubJit.nearCall(); + JITCompiler::Jump done = stubJit.jump(); + + slowPath.link(&stubJit); + stubJit.move(CCallHelpers::TrustedImmPtr(callLinkInfo.callReturnLocation.executableAddress()), GPRInfo::nonArgGPR2); + stubJit.restoreReturnAddressBeforeReturn(GPRInfo::nonArgGPR2); + stubJit.move(calleeGPR, GPRInfo::nonArgGPR0); +#if USE(JSVALUE32_64) + stubJit.move(CCallHelpers::TrustedImm32(JSValue::CellTag), GPRInfo::nonArgGPR1); +#endif + JITCompiler::Jump slow = stubJit.jump(); + + LinkBuffer patchBuffer(*globalData, &stubJit, callerCodeBlock); + + patchBuffer.link(call, FunctionPtr(codePtr.executableAddress())); + patchBuffer.link(done, callLinkInfo.callReturnLocation.labelAtOffset(0)); + patchBuffer.link(slow, CodeLocationLabel(globalData->getCTIStub(virtualCallThunkGenerator).code())); + + RefPtr<ClosureCallStubRoutine> stubRoutine = adoptRef(new ClosureCallStubRoutine( + FINALIZE_DFG_CODE( + patchBuffer, + ("DFG closure call stub for CodeBlock %p, return point %p, target %p (CodeBlock %p)", + callerCodeBlock, callLinkInfo.callReturnLocation.labelAtOffset(0).executableAddress(), + codePtr.executableAddress(), calleeCodeBlock)), + *globalData, callerCodeBlock->ownerExecutable(), structure, executable, callLinkInfo.codeOrigin)); + + RepatchBuffer repatchBuffer(callerCodeBlock); + + repatchBuffer.replaceWithJump( + RepatchBuffer::startOfBranchPtrWithPatchOnRegister(callLinkInfo.hotPathBegin), + CodeLocationLabel(stubRoutine->code().code())); + linkSlowFor(repatchBuffer, globalData, callLinkInfo, CodeForCall); + + callLinkInfo.stub = stubRoutine.release(); + + ASSERT(!calleeCodeBlock || calleeCodeBlock->isIncomingCallAlreadyLinked(&callLinkInfo)); } void dfgResetGetByID(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo) { repatchBuffer.relink(stubInfo.callReturnLocation, operationGetByIdOptimize); - repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall), reinterpret_cast<void*>(-1)); + CodeLocationDataLabelPtr structureLabel = stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall); + if (MacroAssembler::canJumpReplacePatchableBranchPtrWithPatch()) { + repatchBuffer.revertJumpReplacementToPatchableBranchPtrWithPatch( + RepatchBuffer::startOfPatchableBranchPtrWithPatchOnAddress(structureLabel), + MacroAssembler::Address( + static_cast<MacroAssembler::RegisterID>(stubInfo.patch.dfg.baseGPR), + JSCell::structureOffset()), + reinterpret_cast<void*>(-1)); + } + repatchBuffer.repatch(structureLabel, reinterpret_cast<void*>(-1)); #if USE(JSVALUE64) repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.dfg.deltaCallToLoadOrStore), 0); #else @@ -1143,7 +1284,16 @@ void dfgResetPutByID(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo) optimizedFunction = operationPutByIdDirectNonStrictOptimize; } repatchBuffer.relink(stubInfo.callReturnLocation, optimizedFunction); - repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall), reinterpret_cast<void*>(-1)); + CodeLocationDataLabelPtr structureLabel = stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall); + if (MacroAssembler::canJumpReplacePatchableBranchPtrWithPatch()) { + repatchBuffer.revertJumpReplacementToPatchableBranchPtrWithPatch( + RepatchBuffer::startOfPatchableBranchPtrWithPatchOnAddress(structureLabel), + MacroAssembler::Address( + static_cast<MacroAssembler::RegisterID>(stubInfo.patch.dfg.baseGPR), + JSCell::structureOffset()), + reinterpret_cast<void*>(-1)); + } + repatchBuffer.repatch(structureLabel, reinterpret_cast<void*>(-1)); #if USE(JSVALUE64) repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.dfg.deltaCallToLoadOrStore), 0); #else diff --git a/Source/JavaScriptCore/dfg/DFGRepatch.h b/Source/JavaScriptCore/dfg/DFGRepatch.h index 83d4e976d..97d26aab2 100644 --- a/Source/JavaScriptCore/dfg/DFGRepatch.h +++ b/Source/JavaScriptCore/dfg/DFGRepatch.h @@ -41,6 +41,8 @@ void dfgBuildGetByIDProtoList(ExecState*, JSValue, const Identifier&, const Prop void dfgRepatchPutByID(ExecState*, JSValue, const Identifier&, const PutPropertySlot&, StructureStubInfo&, PutKind); void dfgBuildPutByIdList(ExecState*, JSValue, const Identifier&, const PutPropertySlot&, StructureStubInfo&, PutKind); void dfgLinkFor(ExecState*, CallLinkInfo&, CodeBlock*, JSFunction* callee, MacroAssemblerCodePtr, CodeSpecializationKind); +void dfgLinkSlowFor(ExecState*, CallLinkInfo&, CodeSpecializationKind); +void dfgLinkClosureCall(ExecState*, CallLinkInfo&, CodeBlock*, Structure*, ExecutableBase*, MacroAssemblerCodePtr); void dfgResetGetByID(RepatchBuffer&, StructureStubInfo&); void dfgResetPutByID(RepatchBuffer&, StructureStubInfo&); diff --git a/Source/JavaScriptCore/dfg/DFGScoreBoard.h b/Source/JavaScriptCore/dfg/DFGScoreBoard.h index 430bdf552..9b509fe2a 100644 --- a/Source/JavaScriptCore/dfg/DFGScoreBoard.h +++ b/Source/JavaScriptCore/dfg/DFGScoreBoard.h @@ -113,7 +113,7 @@ public: ASSERT(m_used[index] != max()); if (node.refCount() == ++m_used[index]) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Freeing virtual register %u.", index); + dataLogF(" Freeing virtual register %u.", index); #endif // If the use count in the scoreboard reaches the use count for the node, // then this was its last use; the virtual register is now free. @@ -122,7 +122,7 @@ public: m_free.append(index); } else { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Virtual register %u is at %u/%u uses.", index, m_used[index], node.refCount()); + dataLogF(" Virtual register %u is at %u/%u uses.", index, m_used[index], node.refCount()); #endif } } @@ -148,26 +148,26 @@ public: #ifndef NDEBUG void dump() { - dataLog(" USED: [ "); + dataLogF(" USED: [ "); for (unsigned i = 0; i < m_used.size(); ++i) { if (!m_free.contains(i)) { - dataLog("%d:", i); + dataLogF("%d:", i); if (m_used[i] == max()) - dataLog("local "); + dataLogF("local "); else - dataLog("%d ", m_used[i]); + dataLogF("%d ", m_used[i]); } } - dataLog("]\n"); + dataLogF("]\n"); - dataLog(" FREE: [ "); + dataLogF(" FREE: [ "); for (unsigned i = 0; i < m_used.size(); ++i) { if (m_free.contains(i) && m_used[i] != max()) { ASSERT(!m_used[i]); - dataLog("%d ", i); + dataLogF("%d ", i); } } - dataLog("]\n"); + dataLogF("]\n"); } #endif diff --git a/Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h b/Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h index fa1f888e0..4acd8690a 100644 --- a/Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h +++ b/Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h @@ -49,7 +49,7 @@ public: void generate(SpeculativeJIT* jit) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Generating slow path %p at offset 0x%x\n", this, jit->m_jit.debugOffset()); + dataLogF("Generating slow path %p at offset 0x%x\n", this, jit->m_jit.debugOffset()); #endif m_label = jit->m_jit.label(); jit->m_compileIndex = m_compileIndex; diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 6bedd6d68..41276d233 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -59,7 +59,7 @@ SpeculativeJIT::~SpeculativeJIT() void SpeculativeJIT::emitAllocateJSArray(Structure* structure, GPRReg resultGPR, GPRReg storageGPR, unsigned numElements) { - ASSERT(hasContiguous(structure->indexingType())); + ASSERT(hasUndecided(structure->indexingType()) || hasInt32(structure->indexingType()) || hasDouble(structure->indexingType()) || hasContiguous(structure->indexingType())); GPRTemporary scratch(this); GPRReg scratchGPR = scratch.gpr(); @@ -67,6 +67,7 @@ void SpeculativeJIT::emitAllocateJSArray(Structure* structure, GPRReg resultGPR, unsigned vectorLength = std::max(BASE_VECTOR_LEN, numElements); JITCompiler::JumpList slowCases; + slowCases.append( emitAllocateBasicStorage(TrustedImm32(vectorLength * sizeof(JSValue) + sizeof(IndexingHeader)), storageGPR)); m_jit.subPtr(TrustedImm32(vectorLength * sizeof(JSValue)), storageGPR); @@ -79,6 +80,21 @@ void SpeculativeJIT::emitAllocateJSArray(Structure* structure, GPRReg resultGPR, m_jit.store32(TrustedImm32(numElements), MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); m_jit.store32(TrustedImm32(vectorLength), MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); + if (hasDouble(structure->indexingType()) && numElements < vectorLength) { +#if USE(JSVALUE64) + m_jit.move(TrustedImm64(bitwise_cast<int64_t>(QNaN)), scratchGPR); + for (unsigned i = numElements; i < vectorLength; ++i) + m_jit.store64(scratchGPR, MacroAssembler::Address(storageGPR, sizeof(double) * i)); +#else + EncodedValueDescriptor value; + value.asInt64 = JSValue::encode(JSValue(JSValue::EncodeAsDouble, QNaN)); + for (unsigned i = numElements; i < vectorLength; ++i) { + m_jit.store32(TrustedImm32(value.asBits.tag), MacroAssembler::Address(storageGPR, sizeof(double) * i + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(TrustedImm32(value.asBits.payload), MacroAssembler::Address(storageGPR, sizeof(double) * i + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + } +#endif + } + // I want a slow path that also loads out the storage pointer, and that's // what this custom CallArrayAllocatorSlowPathGenerator gives me. It's a lot // of work for a very small piece of functionality. :-/ @@ -258,7 +274,7 @@ void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs js { ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpeculativeJIT was terminated.\n"); + dataLogF("SpeculativeJIT was terminated.\n"); #endif if (!m_compileOkay) return; @@ -276,7 +292,7 @@ void SpeculativeJIT::terminateSpeculativeExecutionWithConditionalDirection(ExitK { ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpeculativeJIT was terminated.\n"); + dataLogF("SpeculativeJIT was terminated.\n"); #endif if (!m_compileOkay) return; @@ -292,7 +308,7 @@ void SpeculativeJIT::addSlowPathGenerator(PassOwnPtr<SlowPathGenerator> slowPath void SpeculativeJIT::runSlowPathGenerators() { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Running %lu slow path generators.\n", m_slowPathGenerators.size()); + dataLogF("Running %lu slow path generators.\n", m_slowPathGenerators.size()); #endif for (unsigned i = 0; i < m_slowPathGenerators.size(); ++i) m_slowPathGenerators[i]->generate(this); @@ -343,32 +359,49 @@ const TypedArrayDescriptor* SpeculativeJIT::typedArrayDescriptor(ArrayMode array } } +JITCompiler::Jump SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, ArrayMode arrayMode, IndexingType shape, bool invert) +{ + switch (arrayMode.arrayClass()) { + case Array::OriginalArray: { + CRASH(); + JITCompiler::Jump result; // I already know that VC++ takes unkindly to the expression "return Jump()", so I'm doing it this way in anticipation of someone eventually using VC++ to compile the DFG. + return result; + } + + case Array::Array: + m_jit.and32(TrustedImm32(IsArray | IndexingShapeMask), tempGPR); + return m_jit.branch32( + invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(IsArray | shape)); + + default: + m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR); + return m_jit.branch32(invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(shape)); + } +} + JITCompiler::JumpList SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, ArrayMode arrayMode, bool invert) { JITCompiler::JumpList result; 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(invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(ContiguousShape))); - break; - } + case Array::Int32: + return jumpSlowForUnwantedArrayMode(tempGPR, arrayMode, Int32Shape, invert); + + case Array::Double: + return jumpSlowForUnwantedArrayMode(tempGPR, arrayMode, DoubleShape, invert); + + case Array::Contiguous: + return jumpSlowForUnwantedArrayMode(tempGPR, arrayMode, ContiguousShape, invert); + case Array::ArrayStorage: case Array::SlowPutArrayStorage: { + ASSERT(!arrayMode.isJSArrayWithOriginalStructure()); + if (arrayMode.isJSArray()) { if (arrayMode.isSlowPut()) { if (invert) { - JITCompiler::Jump slow = - m_jit.branchTest32( - MacroAssembler::Zero, tempGPR, MacroAssembler::TrustedImm32(IsArray)); + 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( @@ -426,7 +459,7 @@ void SpeculativeJIT::checkArray(Node& node) const TypedArrayDescriptor* result = typedArrayDescriptor(node.arrayMode()); - if (node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))) { + if (node.arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1()))) { noResult(m_compileIndex); return; } @@ -437,6 +470,8 @@ void SpeculativeJIT::checkArray(Node& node) case Array::String: expectedClassInfo = &JSString::s_info; break; + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: case Array::SlowPutArrayStorage: { @@ -528,16 +563,30 @@ void SpeculativeJIT::arrayify(Node& node, GPRReg baseReg, GPRReg propertyReg) // If we're allegedly creating contiguous storage and the index is bogus, then // just don't. - if (node.arrayMode().type() == Array::Contiguous && propertyReg != InvalidGPRReg) { - speculationCheck( - Uncountable, JSValueRegs(), NoNode, - m_jit.branch32( - MacroAssembler::AboveOrEqual, propertyReg, TrustedImm32(MIN_SPARSE_ARRAY_INDEX))); + if (propertyReg != InvalidGPRReg) { + switch (node.arrayMode().type()) { + case Array::Int32: + case Array::Double: + case Array::Contiguous: + speculationCheck( + Uncountable, JSValueRegs(), NoNode, + m_jit.branch32( + MacroAssembler::AboveOrEqual, propertyReg, TrustedImm32(MIN_SPARSE_ARRAY_INDEX))); + break; + default: + break; + } } // Now call out to create the array storage. silentSpillAllRegisters(tempGPR); switch (node.arrayMode().type()) { + case Array::Int32: + callOperation(operationEnsureInt32, tempGPR, baseReg); + break; + case Array::Double: + callOperation(operationEnsureDouble, tempGPR, baseReg); + break; case Array::Contiguous: callOperation(operationEnsureContiguous, tempGPR, baseReg); break; @@ -956,33 +1005,33 @@ static const char* dataFormatString(DataFormat format) void SpeculativeJIT::dump(const char* label) { if (label) - dataLog("<%s>\n", label); + dataLogF("<%s>\n", label); - dataLog(" gprs:\n"); + dataLogF(" gprs:\n"); m_gprs.dump(); - dataLog(" fprs:\n"); + dataLogF(" fprs:\n"); m_fprs.dump(); - dataLog(" VirtualRegisters:\n"); + dataLogF(" VirtualRegisters:\n"); for (unsigned i = 0; i < m_generationInfo.size(); ++i) { GenerationInfo& info = m_generationInfo[i]; if (info.alive()) - dataLog(" % 3d:%s%s", i, dataFormatString(info.registerFormat()), dataFormatString(info.spillFormat())); + dataLogF(" % 3d:%s%s", i, dataFormatString(info.registerFormat()), dataFormatString(info.spillFormat())); else - dataLog(" % 3d:[__][__]", i); + dataLogF(" % 3d:[__][__]", i); if (info.registerFormat() == DataFormatDouble) - dataLog(":fpr%d\n", info.fpr()); + dataLogF(":fpr%d\n", info.fpr()); else if (info.registerFormat() != DataFormatNone #if USE(JSVALUE32_64) && !(info.registerFormat() & DataFormatJS) #endif ) { ASSERT(info.gpr() != InvalidGPRReg); - dataLog(":%s\n", GPRInfo::debugName(info.gpr())); + dataLogF(":%s\n", GPRInfo::debugName(info.gpr())); } else - dataLog("\n"); + dataLogF("\n"); } if (label) - dataLog("</%s>\n", label); + dataLogF("</%s>\n", label); } #endif @@ -994,13 +1043,13 @@ void SpeculativeJIT::checkConsistency() for (gpr_iterator iter = m_gprs.begin(); iter != m_gprs.end(); ++iter) { if (iter.isLocked()) { - dataLog("DFG_CONSISTENCY_CHECK failed: gpr %s is locked.\n", iter.debugName()); + dataLogF("DFG_CONSISTENCY_CHECK failed: gpr %s is locked.\n", iter.debugName()); failed = true; } } for (fpr_iterator iter = m_fprs.begin(); iter != m_fprs.end(); ++iter) { if (iter.isLocked()) { - dataLog("DFG_CONSISTENCY_CHECK failed: fpr %s is locked.\n", iter.debugName()); + dataLogF("DFG_CONSISTENCY_CHECK failed: fpr %s is locked.\n", iter.debugName()); failed = true; } } @@ -1028,7 +1077,7 @@ void SpeculativeJIT::checkConsistency() GPRReg gpr = info.gpr(); ASSERT(gpr != InvalidGPRReg); if (m_gprs.name(gpr) != virtualRegister) { - dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (gpr %s).\n", virtualRegister, GPRInfo::debugName(gpr)); + dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (gpr %s).\n", virtualRegister, GPRInfo::debugName(gpr)); failed = true; } break; @@ -1037,7 +1086,7 @@ void SpeculativeJIT::checkConsistency() FPRReg fpr = info.fpr(); ASSERT(fpr != InvalidFPRReg); if (m_fprs.name(fpr) != virtualRegister) { - dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (fpr %s).\n", virtualRegister, FPRInfo::debugName(fpr)); + dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (fpr %s).\n", virtualRegister, FPRInfo::debugName(fpr)); failed = true; } break; @@ -1053,18 +1102,18 @@ void SpeculativeJIT::checkConsistency() GenerationInfo& info = m_generationInfo[virtualRegister]; #if USE(JSVALUE64) if (iter.regID() != info.gpr()) { - dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); + dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); failed = true; } #else if (!(info.registerFormat() & DataFormatJS)) { if (iter.regID() != info.gpr()) { - dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); + dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); failed = true; } } else { if (iter.regID() != info.tagGPR() && iter.regID() != info.payloadGPR()) { - dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); + dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); failed = true; } } @@ -1078,7 +1127,7 @@ void SpeculativeJIT::checkConsistency() GenerationInfo& info = m_generationInfo[virtualRegister]; if (iter.regID() != info.fpr()) { - dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for fpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); + dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for fpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); failed = true; } } @@ -1496,7 +1545,7 @@ void SpeculativeJIT::compile(BasicBlock& block) #endif #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Setting up state for block #%u: ", m_block); + dataLogF("Setting up state for block #%u: ", m_block); #endif m_stream->appendAndLog(VariableEvent::reset()); @@ -1544,7 +1593,7 @@ void SpeculativeJIT::compile(BasicBlock& block) } #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("\n"); + dataLogF("\n"); #endif for (m_indexInBlock = 0; m_indexInBlock < block.size(); ++m_indexInBlock) { @@ -1554,7 +1603,7 @@ void SpeculativeJIT::compile(BasicBlock& block) m_codeOriginForOSR = node.codeOrigin; if (!node.shouldGenerate()) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpeculativeJIT skipping Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset()); + dataLogF("SpeculativeJIT skipping Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset()); #endif switch (node.op()) { case JSConstant: @@ -1601,7 +1650,7 @@ void SpeculativeJIT::compile(BasicBlock& block) // The exception is the this argument, which we don't really need to be // able to recover. #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("\nRecovery for argument %d: ", i); + dataLogF("\nRecovery for argument %d: ", i); recovery.dump(WTF::dataFile()); #endif inlineCallFrame->arguments[i] = recovery; @@ -1617,7 +1666,7 @@ void SpeculativeJIT::compile(BasicBlock& block) } else { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpeculativeJIT generating Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset()); + dataLogF("SpeculativeJIT generating Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset()); #endif #if DFG_ENABLE(JIT_BREAK_ON_EVERY_NODE) m_jit.breakpoint(); @@ -1642,25 +1691,25 @@ void SpeculativeJIT::compile(BasicBlock& block) #if DFG_ENABLE(DEBUG_VERBOSE) if (node.hasResult()) { GenerationInfo& info = m_generationInfo[node.virtualRegister()]; - dataLog("-> %s, vr#%d", dataFormatToString(info.registerFormat()), (int)node.virtualRegister()); + dataLogF("-> %s, vr#%d", dataFormatToString(info.registerFormat()), (int)node.virtualRegister()); if (info.registerFormat() != DataFormatNone) { if (info.registerFormat() == DataFormatDouble) - dataLog(", %s", FPRInfo::debugName(info.fpr())); + dataLogF(", %s", FPRInfo::debugName(info.fpr())); #if USE(JSVALUE32_64) else if (info.registerFormat() & DataFormatJS) - dataLog(", %s %s", GPRInfo::debugName(info.tagGPR()), GPRInfo::debugName(info.payloadGPR())); + dataLogF(", %s %s", GPRInfo::debugName(info.tagGPR()), GPRInfo::debugName(info.payloadGPR())); #endif else - dataLog(", %s", GPRInfo::debugName(info.gpr())); + dataLogF(", %s", GPRInfo::debugName(info.gpr())); } - dataLog(" "); + dataLogF(" "); } else - dataLog(" "); + dataLogF(" "); #endif } #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("\n"); + dataLogF("\n"); #endif // Make sure that the abstract state is rematerialized for the next node. @@ -1797,6 +1846,85 @@ ValueRecovery SpeculativeJIT::computeValueRecoveryFor(const ValueSource& valueSo return ValueRecovery(); } +void SpeculativeJIT::compileDoublePutByVal(Node& node, SpeculateCellOperand& base, SpeculateStrictInt32Operand& property) +{ + Edge child3 = m_jit.graph().varArgChild(node, 2); + Edge child4 = m_jit.graph().varArgChild(node, 3); + + ArrayMode arrayMode = node.arrayMode(); + + GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); + + SpeculateDoubleOperand value(this, child3); + + FPRReg valueReg = value.fpr(); + + if (!isRealNumberSpeculation(m_state.forNode(child3).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, valueReg, valueReg)); + } + + if (!m_compileOkay) + return; + + StorageOperand storage(this, child4); + GPRReg storageReg = storage.gpr(); + + if (node.op() == PutByValAlias) { + // Store the value to the array. + GPRReg propertyReg = property.gpr(); + FPRReg valueReg = value.fpr(); + m_jit.storeDouble(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight)); + + noResult(m_compileIndex); + return; + } + + GPRTemporary temporary; + GPRReg temporaryReg = temporaryRegisterForPutByVal(temporary, node); + + 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.storeDouble(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight)); + + base.use(); + property.use(); + value.use(); + storage.use(); + + if (arrayMode.isOutOfBounds()) { + addSlowPathGenerator( + slowPathCall( + slowCases, this, + m_jit.codeBlock()->isStrictMode() ? operationPutDoubleByValBeyondArrayBoundsStrict : operationPutDoubleByValBeyondArrayBoundsNonStrict, + NoResult, baseReg, propertyReg, valueReg)); + } + + noResult(m_compileIndex, UseChildrenCalledExplicitly); +} + void SpeculativeJIT::compileGetCharCodeAt(Node& node) { SpeculateCellOperand string(this, node.child1()); @@ -1841,7 +1969,7 @@ void SpeculativeJIT::compileGetByValOnString(Node& node) GPRReg propertyReg = property.gpr(); GPRReg storageReg = storage.gpr(); - ASSERT(ArrayMode(Array::String).alreadyChecked(m_state.forNode(node.child1()))); + ASSERT(ArrayMode(Array::String).alreadyChecked(m_jit.graph(), node, 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()))); @@ -1878,7 +2006,7 @@ void SpeculativeJIT::compileGetByValOnString(Node& node) GeneratedOperandType SpeculativeJIT::checkGeneratedTypeForToInt32(NodeIndex nodeIndex) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("checkGeneratedTypeForToInt32@%d ", nodeIndex); + dataLogF("checkGeneratedTypeForToInt32@%d ", nodeIndex); #endif Node& node = at(nodeIndex); VirtualRegister virtualRegister = node.virtualRegister(); @@ -2250,7 +2378,7 @@ void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor& GPRTemporary result(this); GPRReg resultReg = result.gpr(); - ASSERT(node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))); + ASSERT(node.arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1()))); speculationCheck( Uncountable, JSValueRegs(), NoNode, @@ -2400,7 +2528,7 @@ void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor GPRReg propertyReg = property.gpr(); GPRReg storageReg = storage.gpr(); - ASSERT(node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))); + ASSERT(node.arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1()))); FPRTemporary result(this); FPRReg resultReg = result.fpr(); @@ -2437,7 +2565,7 @@ void SpeculativeJIT::compilePutByValForFloatTypedArray(const TypedArrayDescripto SpeculateDoubleOperand valueOp(this, valueUse); - ASSERT_UNUSED(baseUse, node.arrayMode().alreadyChecked(m_state.forNode(baseUse))); + ASSERT_UNUSED(baseUse, node.arrayMode().alreadyChecked(m_jit.graph(), m_jit.graph()[m_compileIndex], m_state.forNode(baseUse))); GPRTemporary result(this); @@ -2763,7 +2891,7 @@ void SpeculativeJIT::compileAdd(Node& node) return; } - if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) { + if (Node::shouldSpeculateNumberExpectingDefined(at(node.child1()), at(node.child2()))) { SpeculateDoubleOperand op1(this, node.child1()); SpeculateDoubleOperand op2(this, node.child2()); FPRTemporary result(this, op1, op2); @@ -3001,7 +3129,7 @@ void SpeculativeJIT::compileIntegerArithDivForX86(Node& node) void SpeculativeJIT::compileArithMod(Node& node) { - if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) + if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { compileSoftModulo(node); return; @@ -3260,7 +3388,7 @@ void SpeculativeJIT::compileGetByValOnArguments(Node& node) if (!m_compileOkay) return; - ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_state.forNode(node.child1()))); + ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1()))); // Two really lame checks. speculationCheck( @@ -3317,7 +3445,7 @@ void SpeculativeJIT::compileGetArgumentsLength(Node& node) if (!m_compileOkay) return; - ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_state.forNode(node.child1()))); + ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1()))); speculationCheck( Uncountable, JSValueSource(), NoNode, @@ -3336,6 +3464,8 @@ void SpeculativeJIT::compileGetArrayLength(Node& node) const TypedArrayDescriptor* descriptor = typedArrayDescriptor(node.arrayMode()); switch (node.arrayMode().type()) { + case Array::Int32: + case Array::Double: case Array::Contiguous: { StorageOperand storage(this, node.child2()); GPRTemporary result(this, storage); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 446ea7dbe..f1384e269 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -1308,6 +1308,11 @@ public: m_jit.setupArgumentsWithExecState(arg1, TrustedImmPtr(identifier)); return appendCallWithExceptionCheckSetResult(operation, result); } + JITCompiler::Call callOperation(J_DFGOperation_EDA operation, GPRReg result, FPRReg arg1, GPRReg arg2) + { + m_jit.setupArgumentsWithExecState(arg1, arg2); + return appendCallWithExceptionCheckSetResult(operation, result); + } JITCompiler::Call callOperation(J_DFGOperation_EJA operation, GPRReg result, GPRReg arg1, GPRReg arg2) { m_jit.setupArgumentsWithExecState(arg1, arg2); @@ -1363,6 +1368,11 @@ public: m_jit.setupArgumentsWithExecState(TrustedImmPtr(inlineCallFrame)); return appendCallWithExceptionCheckSetResult(operation, result); } + JITCompiler::Call callOperation(C_DFGOperation_ESt operation, GPRReg result, Structure* structure) + { + m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure)); + return appendCallWithExceptionCheckSetResult(operation, result); + } JITCompiler::Call callOperation(S_DFGOperation_J operation, GPRReg result, GPRReg arg1) { m_jit.setupArguments(arg1); @@ -1453,6 +1463,11 @@ public: m_jit.setupArgumentsWithExecState(arg1, arg2, arg3); return appendCallWithExceptionCheck(operation); } + JITCompiler::Call callOperation(V_DFGOperation_EOZD operation, GPRReg arg1, GPRReg arg2, FPRReg arg3) + { + m_jit.setupArgumentsWithExecState(arg1, arg2, arg3); + return appendCallWithExceptionCheck(operation); + } JITCompiler::Call callOperation(V_DFGOperation_EOZJ operation, GPRReg arg1, GPRReg arg2, GPRReg arg3) { m_jit.setupArgumentsWithExecState(arg1, arg2, arg3); @@ -1661,11 +1676,21 @@ public: m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, TrustedImm32(arg1Tag), TrustedImmPtr(identifier)); return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); } + JITCompiler::Call callOperation(J_DFGOperation_EDA operation, GPRReg resultTag, GPRReg resultPayload, FPRReg arg1, GPRReg arg2) + { + m_jit.setupArgumentsWithExecState(arg1, arg2); + return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); + } JITCompiler::Call callOperation(J_DFGOperation_EJA operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2) { m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, arg2); return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); } + JITCompiler::Call callOperation(J_DFGOperation_EJA operation, GPRReg resultTag, GPRReg resultPayload, TrustedImm32 arg1Tag, GPRReg arg1Payload, GPRReg arg2) + { + m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, arg2); + return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); + } JITCompiler::Call callOperation(J_DFGOperation_EJ operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1Tag, GPRReg arg1Payload) { m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag); @@ -1716,6 +1741,11 @@ public: m_jit.setupArgumentsWithExecState(TrustedImmPtr(inlineCallFrame)); return appendCallWithExceptionCheckSetResult(operation, result); } + JITCompiler::Call callOperation(C_DFGOperation_ESt operation, GPRReg result, Structure* structure) + { + m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure)); + return appendCallWithExceptionCheckSetResult(operation, result); + } JITCompiler::Call callOperation(S_DFGOperation_J operation, GPRReg result, GPRReg arg1Tag, GPRReg arg1Payload) { m_jit.setupArguments(arg1Payload, arg1Tag); @@ -1819,11 +1849,21 @@ public: m_jit.setupArgumentsWithExecState(arg1, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag); return appendCallWithExceptionCheck(operation); } + JITCompiler::Call callOperation(V_DFGOperation_EOZD operation, GPRReg arg1, GPRReg arg2, FPRReg arg3) + { + m_jit.setupArgumentsWithExecState(arg1, arg2, arg3); + return appendCallWithExceptionCheck(operation); + } JITCompiler::Call callOperation(V_DFGOperation_EOZJ operation, GPRReg arg1, GPRReg arg2, GPRReg arg3Tag, GPRReg arg3Payload) { m_jit.setupArgumentsWithExecState(arg1, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag); return appendCallWithExceptionCheck(operation); } + JITCompiler::Call callOperation(V_DFGOperation_EOZJ operation, GPRReg arg1, GPRReg arg2, TrustedImm32 arg3Tag, GPRReg arg3Payload) + { + m_jit.setupArgumentsWithExecState(arg1, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag); + return appendCallWithExceptionCheck(operation); + } JITCompiler::Call callOperation(V_DFGOperation_W operation, WatchpointSet* watchpointSet) { m_jit.setupArguments(TrustedImmPtr(watchpointSet)); @@ -2270,6 +2310,11 @@ public: void compileAllocatePropertyStorage(Node&); void compileReallocatePropertyStorage(Node&); +#if USE(JSVALUE32_64) + template<typename BaseOperandType, typename PropertyOperandType, typename ValueOperandType, typename TagType> + void compileContiguousPutByVal(Node&, BaseOperandType&, PropertyOperandType&, ValueOperandType&, GPRReg valuePayloadReg, TagType valueTag); +#endif + void compileDoublePutByVal(Node&, SpeculateCellOperand& base, SpeculateStrictInt32Operand& property); bool putByValWillNeedExtraRegister(ArrayMode arrayMode) { return arrayMode.mayStoreToHole(); @@ -2415,6 +2460,7 @@ public: const TypedArrayDescriptor* typedArrayDescriptor(ArrayMode); + JITCompiler::Jump jumpSlowForUnwantedArrayMode(GPRReg tempWithIndexingTypeReg, ArrayMode, IndexingType, bool invert); JITCompiler::JumpList jumpSlowForUnwantedArrayMode(GPRReg tempWithIndexingTypeReg, ArrayMode, bool invert = false); void checkArray(Node&); void arrayify(Node&, GPRReg baseReg, GPRReg propertyReg); @@ -2955,6 +3001,11 @@ public: m_gprOrInvalid = m_jit->fillSpeculateInt(index(), m_format); return m_gprOrInvalid; } + + void use() + { + m_jit->use(m_index); + } private: SpeculativeJIT* m_jit; @@ -3035,6 +3086,11 @@ public: m_fprOrInvalid = m_jit->fillSpeculateDouble(index()); return m_fprOrInvalid; } + + void use() + { + m_jit->use(m_index); + } private: SpeculativeJIT* m_jit; diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index 65fdf5593..05af6962e 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -29,9 +29,11 @@ #if ENABLE(DFG_JIT) +#include "ArrayPrototype.h" #include "DFGCallArrayAllocatorSlowPathGenerator.h" #include "DFGSlowPathGenerator.h" #include "JSActivation.h" +#include "ObjectPrototype.h" namespace JSC { namespace DFG { @@ -996,7 +998,6 @@ void SpeculativeJIT::nonSpeculativeNonPeepholeStrictEq(Node& node, bool invert) void SpeculativeJIT::emitCall(Node& node) { - if (node.op() != Call) ASSERT(node.op() == Construct); @@ -1047,8 +1048,8 @@ void SpeculativeJIT::emitCall(Node& node) m_jit.addPtr(TrustedImm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister); - slowPath.append(m_jit.branchPtrWithPatch(MacroAssembler::NotEqual, calleePayloadGPR, targetToCheck)); slowPath.append(m_jit.branch32(MacroAssembler::NotEqual, calleeTagGPR, TrustedImm32(JSValue::CellTag))); + slowPath.append(m_jit.branchPtrWithPatch(MacroAssembler::NotEqual, calleePayloadGPR, targetToCheck)); m_jit.loadPtr(MacroAssembler::Address(calleePayloadGPR, OBJECT_OFFSETOF(JSFunction, m_scope)), resultPayloadGPR); m_jit.storePtr(resultPayloadGPR, MacroAssembler::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::ScopeChain + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); m_jit.store32(MacroAssembler::TrustedImm32(JSValue::CellTag), MacroAssembler::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::ScopeChain + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); @@ -1082,14 +1083,14 @@ void SpeculativeJIT::emitCall(Node& node) jsValueResult(resultTagGPR, resultPayloadGPR, m_compileIndex, DataFormatJS, UseChildrenCalledExplicitly); - m_jit.addJSCall(fastCall, slowCall, targetToCheck, callType, at(m_compileIndex).codeOrigin); + m_jit.addJSCall(fastCall, slowCall, targetToCheck, callType, calleePayloadGPR, at(m_compileIndex).codeOrigin); } template<bool strict> GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& returnFormat) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecInt@%d ", nodeIndex); + dataLogF("SpecInt@%d ", nodeIndex); #endif if (isKnownNotInteger(nodeIndex)) { terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); @@ -1187,7 +1188,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntStrict(NodeIndex nodeIndex) FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecDouble@%d ", nodeIndex); + dataLogF("SpecDouble@%d ", nodeIndex); #endif if (isKnownNotNumber(nodeIndex)) { terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); @@ -1322,7 +1323,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpeculation) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecCell@%d ", nodeIndex); + dataLogF("SpecCell@%d ", nodeIndex); #endif if (isKnownNotCell(nodeIndex)) { terminateSpeculativeExecutionWithConditionalDirection(Uncountable, JSValueRegs(), NoNode, isForwardSpeculation); @@ -1397,7 +1398,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpec GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecBool@%d ", nodeIndex); + dataLogF("SpecBool@%d ", nodeIndex); #endif SpeculatedType type = m_state.forNode(nodeIndex).m_type; Node& node = m_jit.graph()[nodeIndex]; @@ -2046,6 +2047,69 @@ void SpeculativeJIT::emitBranch(Node& node) } } +template<typename BaseOperandType, typename PropertyOperandType, typename ValueOperandType, typename TagType> +void SpeculativeJIT::compileContiguousPutByVal(Node& node, BaseOperandType& base, PropertyOperandType& property, ValueOperandType& value, GPRReg valuePayloadReg, TagType valueTag) +{ + Edge child4 = m_jit.graph().varArgChild(node, 3); + + ArrayMode arrayMode = node.arrayMode(); + + GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); + + StorageOperand storage(this, child4); + GPRReg storageReg = storage.gpr(); + + if (node.op() == PutByValAlias) { + // Store the value to the array. + GPRReg propertyReg = property.gpr(); + m_jit.store32(valueTag, 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))); + + noResult(m_compileIndex); + return; + } + + 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(valueTag, 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 (arrayMode.isOutOfBounds()) { + addSlowPathGenerator( + slowPathCall( + slowCases, this, + m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict, + NoResult, baseReg, propertyReg, valueTag, valuePayloadReg)); + } + + noResult(m_compileIndex, UseChildrenCalledExplicitly); +} + void SpeculativeJIT::compile(Node& node) { NodeType op = node.op(); @@ -2064,6 +2128,18 @@ void SpeculativeJIT::compile(Node& node) initConstantInfo(m_compileIndex); break; + case Identity: { + // This could be done a lot better. We take the cheap way out because Identity + // is only going to stick around after CSE if we had prediction weirdness. + JSValueOperand operand(this, node.child1()); + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + m_jit.move(operand.tagGPR(), resultTag.gpr()); + m_jit.move(operand.payloadGPR(), resultPayload.gpr()); + jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); + break; + } + case GetLocal: { SpeculatedType prediction = node.variableAccessData()->prediction(); AbstractValue& value = block()->valuesAtHead.operand(node.local()); @@ -2366,7 +2442,8 @@ void SpeculativeJIT::compile(Node& node) break; case ArithDiv: { - if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { + if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2())) + && node.canSpeculateInteger()) { #if CPU(X86) compileIntegerArithDivForX86(node); #else // CPU(X86) -> so non-X86 code follows @@ -2393,7 +2470,8 @@ void SpeculativeJIT::compile(Node& node) } case ArithAbs: { - if (at(node.child1()).shouldSpeculateInteger() && node.canSpeculateInteger()) { + if (at(node.child1()).shouldSpeculateIntegerForArithmetic() + && node.canSpeculateInteger()) { SpeculateIntegerOperand op1(this, node.child1()); GPRTemporary result(this, op1); GPRTemporary scratch(this); @@ -2417,7 +2495,8 @@ void SpeculativeJIT::compile(Node& node) case ArithMin: case ArithMax: { - if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { + if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2())) + && node.canSpeculateInteger()) { SpeculateStrictInt32Operand op1(this, node.child1()); SpeculateStrictInt32Operand op2(this, node.child2()); GPRTemporary result(this, op1); @@ -2567,6 +2646,7 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); break; } + case Array::Int32: case Array::Contiguous: { if (node.arrayMode().isInBounds()) { SpeculateStrictInt32Operand property(this, node.child2()); @@ -2580,8 +2660,20 @@ void SpeculativeJIT::compile(Node& node) speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); - GPRTemporary resultTag(this); GPRTemporary resultPayload(this); + if (node.arrayMode().type() == Array::Int32) { + speculationCheck( + OutOfBounds, JSValueRegs(), NoNode, + m_jit.branch32( + MacroAssembler::Equal, + MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), + TrustedImm32(JSValue::EmptyValueTag))); + m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr()); + integerResult(resultPayload.gpr(), m_compileIndex); + break; + } + + GPRTemporary resultTag(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()); @@ -2621,6 +2713,68 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(resultTagReg, resultPayloadReg, m_compileIndex); break; } + case Array::Double: { + if (node.arrayMode().isInBounds()) { + if (node.arrayMode().isSaneChain()) { + JSGlobalObject* globalObject = m_jit.globalObjectFor(node.codeOrigin); + ASSERT(globalObject->arrayPrototypeChainIsSane()); + globalObject->arrayPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint()); + globalObject->objectPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint()); + } + + 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()))); + + FPRTemporary result(this); + m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.fpr()); + if (!node.arrayMode().isSaneChain()) + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, result.fpr(), result.fpr())); + doubleResult(result.fpr(), m_compileIndex); + break; + } + + SpeculateCellOperand base(this, node.child1()); + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); + + GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); + + if (!m_compileOkay) + return; + + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + FPRTemporary temp(this); + GPRReg resultTagReg = resultTag.gpr(); + GPRReg resultPayloadReg = resultPayload.gpr(); + FPRReg tempReg = temp.fpr(); + + MacroAssembler::JumpList slowCases; + + slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + + m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), tempReg); + slowCases.append(m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempReg, tempReg)); + boxDouble(tempReg, resultTagReg, resultPayloadReg); + + addSlowPathGenerator( + slowPathCall( + slowCases, this, operationGetByValArrayInt, + JSValueRegs(resultTagReg, resultPayloadReg), baseReg, propertyReg)); + + jsValueResult(resultTagReg, resultPayloadReg, m_compileIndex); + break; + } case Array::ArrayStorage: case Array::SlowPutArrayStorage: { if (node.arrayMode().isInBounds()) { @@ -2771,6 +2925,17 @@ void SpeculativeJIT::compile(Node& node) GPRReg propertyReg = property.gpr(); switch (arrayMode.type()) { + case Array::Int32: { + SpeculateIntegerOperand value(this, child3); + + GPRReg valuePayloadReg = value.gpr(); + + if (!m_compileOkay) + return; + + compileContiguousPutByVal(node, base, property, value, valuePayloadReg, TrustedImm32(JSValue::Int32Tag)); + break; + } case Array::Contiguous: { JSValueOperand value(this, child3); @@ -2784,61 +2949,14 @@ void SpeculativeJIT::compile(Node& node) GPRTemporary scratch(this); writeBarrier(baseReg, valueTagReg, child3, WriteBarrierForPropertyAccess, scratch.gpr()); } - - StorageOperand storage(this, child4); - GPRReg storageReg = storage.gpr(); - - if (node.op() == PutByValAlias) { - // Store the value to the array. - GPRReg propertyReg = property.gpr(); - 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))); - - noResult(m_compileIndex); - break; - } - - 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 (arrayMode.isOutOfBounds()) { - addSlowPathGenerator( - slowPathCall( - slowCases, this, - m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict, - NoResult, baseReg, propertyReg, valueTagReg, valuePayloadReg)); - } - - noResult(m_compileIndex, UseChildrenCalledExplicitly); + compileContiguousPutByVal(node, base, property, value, valuePayloadReg, valueTagReg); + break; + } + case Array::Double: { + compileDoublePutByVal(node, base, property); break; } - case Array::ArrayStorage: case Array::SlowPutArrayStorage: { JSValueOperand value(this, child3); @@ -3028,24 +3146,47 @@ void SpeculativeJIT::compile(Node& node) ASSERT(node.arrayMode().isJSArray()); SpeculateCellOperand base(this, node.child1()); - JSValueOperand value(this, node.child2()); GPRTemporary storageLength(this); GPRReg baseGPR = base.gpr(); - GPRReg valueTagGPR = value.tagGPR(); - GPRReg valuePayloadGPR = value.payloadGPR(); GPRReg storageLengthGPR = storageLength.gpr(); - if (Heap::isWriteBarrierEnabled()) { - GPRTemporary scratch(this); - writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); - } - StorageOperand storage(this, node.child3()); GPRReg storageGPR = storage.gpr(); switch (node.arrayMode().type()) { + case Array::Int32: { + SpeculateIntegerOperand value(this, node.child2()); + GPRReg valuePayloadGPR = value.gpr(); + + 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(TrustedImm32(JSValue::Int32Tag), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(valuePayloadGPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + m_jit.add32(TrustedImm32(1), storageLengthGPR); + m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); + m_jit.move(TrustedImm32(JSValue::Int32Tag), storageGPR); + + addSlowPathGenerator( + slowPathCall( + slowPath, this, operationArrayPush, + JSValueRegs(storageGPR, storageLengthGPR), + TrustedImm32(JSValue::Int32Tag), valuePayloadGPR, baseGPR)); + + jsValueResult(storageGPR, storageLengthGPR, m_compileIndex); + break; + } + case Array::Contiguous: { + JSValueOperand value(this, node.child2()); + GPRReg valueTagGPR = value.tagGPR(); + GPRReg valuePayloadGPR = value.payloadGPR(); + + if (Heap::isWriteBarrierEnabled()) { + GPRTemporary scratch(this); + writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); + } + 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))); @@ -3064,7 +3205,45 @@ void SpeculativeJIT::compile(Node& node) break; } + case Array::Double: { + SpeculateDoubleOperand value(this, node.child2()); + FPRReg valueFPR = value.fpr(); + + if (!isRealNumberSpeculation(m_state.forNode(node.child2()).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, valueFPR, valueFPR)); + } + + 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.storeDouble(valueFPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight)); + m_jit.add32(TrustedImm32(1), storageLengthGPR); + m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); + m_jit.move(TrustedImm32(JSValue::Int32Tag), storageGPR); + + addSlowPathGenerator( + slowPathCall( + slowPath, this, operationArrayPushDouble, + JSValueRegs(storageGPR, storageLengthGPR), + valueFPR, baseGPR)); + + jsValueResult(storageGPR, storageLengthGPR, m_compileIndex); + break; + } + case Array::ArrayStorage: { + JSValueOperand value(this, node.child2()); + GPRReg valueTagGPR = value.tagGPR(); + GPRReg valuePayloadGPR = value.payloadGPR(); + + if (Heap::isWriteBarrierEnabled()) { + GPRTemporary scratch(this); + writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); + } + m_jit.load32(MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset()), storageLengthGPR); // Refuse to handle bizarre lengths. @@ -3107,6 +3286,7 @@ void SpeculativeJIT::compile(Node& node) GPRReg storageGPR = storage.gpr(); switch (node.arrayMode().type()) { + case Array::Int32: case Array::Contiguous: { m_jit.load32( MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), valuePayloadGPR); @@ -3140,6 +3320,44 @@ void SpeculativeJIT::compile(Node& node) break; } + case Array::Double: { + FPRTemporary temp(this); + FPRReg tempFPR = temp.fpr(); + + m_jit.load32( + MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), valuePayloadGPR); + MacroAssembler::Jump undefinedCase = + m_jit.branchTest32(MacroAssembler::Zero, valuePayloadGPR); + m_jit.sub32(TrustedImm32(1), valuePayloadGPR); + m_jit.store32( + valuePayloadGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); + m_jit.loadDouble( + MacroAssembler::BaseIndex(storageGPR, valuePayloadGPR, MacroAssembler::TimesEight), + tempFPR); + MacroAssembler::Jump slowCase = m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempFPR, tempFPR); + JSValue nan = JSValue(JSValue::EncodeAsDouble, QNaN); + m_jit.store32( + MacroAssembler::TrustedImm32(nan.u.asBits.tag), + MacroAssembler::BaseIndex(storageGPR, valuePayloadGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32( + MacroAssembler::TrustedImm32(nan.u.asBits.payload), + MacroAssembler::BaseIndex(storageGPR, valuePayloadGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + boxDouble(tempFPR, valueTagGPR, valuePayloadGPR); + + addSlowPathGenerator( + slowPathMove( + undefinedCase, this, + MacroAssembler::TrustedImm32(jsUndefined().tag()), valueTagGPR, + MacroAssembler::TrustedImm32(jsUndefined().payload()), valuePayloadGPR)); + addSlowPathGenerator( + slowPathCall( + slowCase, this, operationArrayPopAndRecoverLength, + JSValueRegs(valueTagGPR, valuePayloadGPR), baseGPR)); + + jsValueResult(valueTagGPR, valuePayloadGPR, m_compileIndex); + break; + } + case Array::ArrayStorage: { GPRTemporary storageLength(this); GPRReg storageLengthGPR = storageLength.gpr(); @@ -3358,11 +3576,17 @@ void SpeculativeJIT::compile(Node& node) case NewArray: { JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin); - if (!globalObject->isHavingABadTime()) { + if (!globalObject->isHavingABadTime() && !hasArrayStorage(node.indexingType())) { globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint()); - ASSERT(hasContiguous(globalObject->arrayStructure()->indexingType())); - + Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()); + ASSERT(structure->indexingType() == node.indexingType()); + ASSERT( + hasUndecided(structure->indexingType()) + || hasInt32(structure->indexingType()) + || hasDouble(structure->indexingType()) + || hasContiguous(structure->indexingType())); + unsigned numElements = node.numChildren(); GPRTemporary result(this); @@ -3371,17 +3595,52 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); GPRReg storageGPR = storage.gpr(); - emitAllocateJSArray(globalObject->arrayStructure(), resultGPR, storageGPR, numElements); + emitAllocateJSArray(structure, resultGPR, storageGPR, numElements); // At this point, one way or another, resultGPR and storageGPR have pointers to // the JSArray and the Butterfly, respectively. + ASSERT(!hasUndecided(structure->indexingType()) || !node.numChildren()); + for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) { - JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]); - GPRReg opTagGPR = operand.tagGPR(); - GPRReg opPayloadGPR = operand.payloadGPR(); - m_jit.store32(opTagGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); - m_jit.store32(opPayloadGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + Edge use = m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]; + switch (node.indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + CRASH(); + break; + case ALL_DOUBLE_INDEXING_TYPES: { + SpeculateDoubleOperand operand(this, use); + FPRReg opFPR = operand.fpr(); + if (!isRealNumberSpeculation(m_state.forNode(use).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR)); + } + + m_jit.storeDouble(opFPR, MacroAssembler::Address(storageGPR, sizeof(double) * operandIdx)); + break; + } + case ALL_INT32_INDEXING_TYPES: { + SpeculateIntegerOperand operand(this, use); + m_jit.store32(TrustedImm32(JSValue::Int32Tag), MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(operand.gpr(), MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + break; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { + JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]); + GPRReg opTagGPR = operand.tagGPR(); + GPRReg opPayloadGPR = operand.payloadGPR(); + m_jit.store32(opTagGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(opPayloadGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + break; + } + default: + CRASH(); + break; + } } // Yuck, we should *really* have a way of also returning the storageGPR. But @@ -3399,7 +3658,7 @@ void SpeculativeJIT::compile(Node& node) flushRegisters(); GPRResult result(this); callOperation( - operationNewEmptyArray, result.gpr(), globalObject->arrayStructure()); + operationNewEmptyArray, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())); cellResult(result.gpr(), m_compileIndex); break; } @@ -3409,13 +3668,61 @@ void SpeculativeJIT::compile(Node& node) EncodedJSValue* buffer = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0; for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) { - JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]); - GPRReg opTagGPR = operand.tagGPR(); - GPRReg opPayloadGPR = operand.payloadGPR(); - operand.use(); - - m_jit.store32(opTagGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)); - m_jit.store32(opPayloadGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); + // Need to perform the speculations that this node promises to perform. If we're + // emitting code here and the indexing type is not array storage then there is + // probably something hilarious going on and we're already failing at all the + // things, but at least we're going to be sound. + Edge use = m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]; + switch (node.indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + CRASH(); + break; + case ALL_DOUBLE_INDEXING_TYPES: { + SpeculateDoubleOperand operand(this, use); + FPRReg opFPR = operand.fpr(); + if (!isRealNumberSpeculation(m_state.forNode(use).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR)); + } + + m_jit.storeDouble(opFPR, reinterpret_cast<char*>(buffer + operandIdx)); + break; + } + case ALL_INT32_INDEXING_TYPES: { + SpeculateIntegerOperand operand(this, use); + GPRReg opGPR = operand.gpr(); + m_jit.store32(TrustedImm32(JSValue::Int32Tag), reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)); + m_jit.store32(opGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); + break; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]); + GPRReg opTagGPR = operand.tagGPR(); + GPRReg opPayloadGPR = operand.payloadGPR(); + + m_jit.store32(opTagGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)); + m_jit.store32(opPayloadGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); + operand.use(); + break; + } + default: + CRASH(); + break; + } + } + + switch (node.indexingType()) { + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + useChildren(node); + break; + default: + break; } flushRegisters(); @@ -3431,8 +3738,8 @@ void SpeculativeJIT::compile(Node& node) GPRResult result(this); callOperation( - operationNewArray, result.gpr(), globalObject->arrayStructure(), - static_cast<void *>(buffer), node.numChildren()); + operationNewArray, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), + static_cast<void*>(buffer), node.numChildren()); if (scratchSize) { GPRTemporary scratch(this); @@ -3471,17 +3778,30 @@ void SpeculativeJIT::compile(Node& node) emitAllocateBasicStorage(resultGPR, storageGPR)); m_jit.subPtr(scratchGPR, storageGPR); emitAllocateBasicJSObject<JSArray, MarkedBlock::None>( - TrustedImmPtr(globalObject->arrayStructure()), resultGPR, scratchGPR, + TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())), resultGPR, scratchGPR, storageGPR, sizeof(JSArray), slowCases); m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); + if (hasDouble(node.indexingType())) { + JSValue nan = JSValue(JSValue::EncodeAsDouble, QNaN); + + m_jit.move(sizeGPR, scratchGPR); + MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratchGPR); + MacroAssembler::Label loop = m_jit.label(); + m_jit.sub32(TrustedImm32(1), scratchGPR); + m_jit.store32(TrustedImm32(nan.u.asBits.tag), MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(TrustedImm32(nan.u.asBits.payload), MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + m_jit.branchTest32(MacroAssembler::NonZero, scratchGPR).linkTo(loop, &m_jit); + done.link(&m_jit); + } + addSlowPathGenerator(adoptPtr( new CallArrayAllocatorWithVariableSizeSlowPathGenerator( slowCases, this, operationNewArrayWithSize, resultGPR, - globalObject->arrayStructure(), - globalObject->arrayStructureWithArrayStorage(), + globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), + globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage), sizeGPR))); cellResult(resultGPR, m_compileIndex); @@ -3492,15 +3812,24 @@ void SpeculativeJIT::compile(Node& node) GPRReg sizeGPR = size.gpr(); flushRegisters(); GPRResult result(this); + GPRReg resultGPR = result.gpr(); + GPRReg structureGPR = selectScratchGPR(sizeGPR); + MacroAssembler::Jump bigLength = m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX)); + m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())), structureGPR); + MacroAssembler::Jump done = m_jit.jump(); + bigLength.link(&m_jit); + m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage)), structureGPR); + done.link(&m_jit); callOperation( - operationNewArrayWithSize, result.gpr(), globalObject->arrayStructure(), sizeGPR); - cellResult(result.gpr(), m_compileIndex); + operationNewArrayWithSize, resultGPR, structureGPR, sizeGPR); + cellResult(resultGPR, m_compileIndex); break; } case NewArrayBuffer: { JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin); - if (!globalObject->isHavingABadTime()) { + IndexingType indexingType = node.indexingType(); + if (!globalObject->isHavingABadTime() && !hasArrayStorage(indexingType)) { globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint()); unsigned numElements = node.numConstants(); @@ -3511,12 +3840,25 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); GPRReg storageGPR = storage.gpr(); - emitAllocateJSArray(globalObject->arrayStructure(), resultGPR, storageGPR, numElements); + emitAllocateJSArray(globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType), resultGPR, storageGPR, numElements); - int32_t* data = bitwise_cast<int32_t*>(m_jit.codeBlock()->constantBuffer(node.startConstant())); - for (unsigned index = 0; index < node.numConstants() * 2; ++index) { - m_jit.store32( - Imm32(data[index]), MacroAssembler::Address(storageGPR, sizeof(int32_t) * index)); + if (node.indexingType() == ArrayWithDouble) { + JSValue* data = m_jit.codeBlock()->constantBuffer(node.startConstant()); + for (unsigned index = 0; index < node.numConstants(); ++index) { + union { + int32_t halves[2]; + double value; + } u; + u.value = data[index].asNumber(); + m_jit.store32(Imm32(u.halves[0]), MacroAssembler::Address(storageGPR, sizeof(double) * index)); + m_jit.store32(Imm32(u.halves[1]), MacroAssembler::Address(storageGPR, sizeof(double) * index + sizeof(int32_t))); + } + } else { + int32_t* data = bitwise_cast<int32_t*>(m_jit.codeBlock()->constantBuffer(node.startConstant())); + for (unsigned index = 0; index < node.numConstants() * 2; ++index) { + m_jit.store32( + Imm32(data[index]), MacroAssembler::Address(storageGPR, sizeof(int32_t) * index)); + } } cellResult(resultGPR, m_compileIndex); @@ -3526,7 +3868,7 @@ void SpeculativeJIT::compile(Node& node) flushRegisters(); GPRResult result(this); - callOperation(operationNewArrayBuffer, result.gpr(), globalObject->arrayStructure(), node.startConstant(), node.numConstants()); + callOperation(operationNewArrayBuffer, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), node.startConstant(), node.numConstants()); cellResult(result.gpr(), m_compileIndex); break; @@ -3630,6 +3972,12 @@ void SpeculativeJIT::compile(Node& node) break; } + case InheritorIDWatchpoint: { + jsCast<JSFunction*>(node.function())->addInheritorIDWatchpoint(speculationWatchpoint()); + noResult(m_compileIndex); + break; + } + case NewObject: { GPRTemporary result(this); GPRTemporary scratch(this); @@ -3639,9 +3987,9 @@ void SpeculativeJIT::compile(Node& node) MacroAssembler::JumpList slowPath; - emitAllocateJSFinalObject(MacroAssembler::TrustedImmPtr(m_jit.globalObjectFor(node.codeOrigin)->emptyObjectStructure()), resultGPR, scratchGPR, slowPath); + emitAllocateJSFinalObject(MacroAssembler::TrustedImmPtr(node.structure()), resultGPR, scratchGPR, slowPath); - addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR)); + addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR, node.structure())); cellResult(resultGPR, m_compileIndex); break; @@ -3810,7 +4158,7 @@ void SpeculativeJIT::compile(Node& node) case CheckFunction: { SpeculateCellOperand function(this, node.child1()); - speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, function.gpr(), node.function())); + speculationCheck(BadCache, JSValueSource::unboxedCell(function.gpr()), node.child1(), m_jit.branchWeakPtr(JITCompiler::NotEqual, function.gpr(), node.function())); noResult(m_compileIndex); break; } diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index 6c066c388..da6583c70 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -29,8 +29,10 @@ #if ENABLE(DFG_JIT) #include "Arguments.h" +#include "ArrayPrototype.h" #include "DFGCallArrayAllocatorSlowPathGenerator.h" #include "DFGSlowPathGenerator.h" +#include "ObjectPrototype.h" namespace JSC { namespace DFG { @@ -1056,14 +1058,14 @@ void SpeculativeJIT::emitCall(Node& node) jsValueResult(resultGPR, m_compileIndex, DataFormatJS, UseChildrenCalledExplicitly); - m_jit.addJSCall(fastCall, slowCall, targetToCheck, callType, at(m_compileIndex).codeOrigin); + m_jit.addJSCall(fastCall, slowCall, targetToCheck, callType, calleeGPR, at(m_compileIndex).codeOrigin); } template<bool strict> GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& returnFormat) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecInt@%d ", nodeIndex); + dataLogF("SpecInt@%d ", nodeIndex); #endif SpeculatedType type = m_state.forNode(nodeIndex).m_type; Node& node = at(nodeIndex); @@ -1211,7 +1213,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntStrict(NodeIndex nodeIndex) FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecDouble@%d ", nodeIndex); + dataLogF("SpecDouble@%d ", nodeIndex); #endif SpeculatedType type = m_state.forNode(nodeIndex).m_type; Node& node = at(nodeIndex); @@ -1365,7 +1367,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpeculation) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecCell@%d ", nodeIndex); + dataLogF("SpecCell@%d ", nodeIndex); #endif SpeculatedType type = m_state.forNode(nodeIndex).m_type; Node& node = at(nodeIndex); @@ -1441,7 +1443,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpec GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecBool@%d ", nodeIndex); + dataLogF("SpecBool@%d ", nodeIndex); #endif SpeculatedType type = m_state.forNode(nodeIndex).m_type; Node& node = at(nodeIndex); @@ -2128,6 +2130,16 @@ void SpeculativeJIT::compile(Node& node) m_jit.addWeakReference(node.weakConstant()); initConstantInfo(m_compileIndex); break; + + case Identity: { + // This could be done a lot better. We take the cheap way out because Identity + // is only going to stick around after CSE if we had prediction weirdness. + JSValueOperand operand(this, node.child1()); + GPRTemporary result(this, operand); + m_jit.move(operand.gpr(), result.gpr()); + jsValueResult(result.gpr(), m_compileIndex); + break; + } case GetLocal: { SpeculatedType prediction = node.variableAccessData()->prediction(); @@ -2403,7 +2415,8 @@ void SpeculativeJIT::compile(Node& node) break; case ArithDiv: { - if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { + if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2())) + && node.canSpeculateInteger()) { compileIntegerArithDivForX86(node); break; } @@ -2426,7 +2439,8 @@ void SpeculativeJIT::compile(Node& node) } case ArithAbs: { - if (at(node.child1()).shouldSpeculateInteger() && node.canSpeculateInteger()) { + if (at(node.child1()).shouldSpeculateIntegerForArithmetic() + && node.canSpeculateInteger()) { SpeculateIntegerOperand op1(this, node.child1()); GPRTemporary result(this); GPRTemporary scratch(this); @@ -2450,7 +2464,8 @@ void SpeculativeJIT::compile(Node& node) case ArithMin: case ArithMax: { - if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { + if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2())) + && node.canSpeculateInteger()) { SpeculateStrictInt32Operand op1(this, node.child1()); SpeculateStrictInt32Operand op2(this, node.child2()); GPRTemporary result(this, op1); @@ -2598,6 +2613,7 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(result.gpr(), m_compileIndex); break; } + case Array::Int32: case Array::Contiguous: { if (node.arrayMode().isInBounds()) { SpeculateStrictInt32Operand property(this, node.child2()); @@ -2614,7 +2630,7 @@ void SpeculativeJIT::compile(Node& node) 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); + jsValueResult(result.gpr(), m_compileIndex, node.arrayMode().type() == Array::Int32 ? DataFormatJSInteger : DataFormatJS); break; } @@ -2647,6 +2663,68 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(resultReg, m_compileIndex); break; } + + case Array::Double: { + if (node.arrayMode().isInBounds()) { + if (node.arrayMode().isSaneChain()) { + JSGlobalObject* globalObject = m_jit.globalObjectFor(node.codeOrigin); + ASSERT(globalObject->arrayPrototypeChainIsSane()); + globalObject->arrayPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint()); + globalObject->objectPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint()); + } + + 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()))); + + FPRTemporary result(this); + m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.fpr()); + if (!node.arrayMode().isSaneChain()) + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, result.fpr(), result.fpr())); + doubleResult(result.fpr(), m_compileIndex); + break; + } + + SpeculateCellOperand base(this, node.child1()); + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); + + GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); + + if (!m_compileOkay) + return; + + GPRTemporary result(this); + FPRTemporary temp(this); + GPRReg resultReg = result.gpr(); + FPRReg tempReg = temp.fpr(); + + MacroAssembler::JumpList slowCases; + + slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + + m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), tempReg); + slowCases.append(m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempReg, tempReg)); + boxDouble(tempReg, resultReg); + + addSlowPathGenerator( + slowPathCall( + slowCases, this, operationGetByValArrayInt, + result.gpr(), baseReg, propertyReg)); + + jsValueResult(resultReg, m_compileIndex); + break; + } + case Array::ArrayStorage: case Array::SlowPutArrayStorage: { if (node.arrayMode().isInBounds()) { @@ -2789,6 +2867,7 @@ void SpeculativeJIT::compile(Node& node) GPRReg propertyReg = property.gpr(); switch (arrayMode.type()) { + case Array::Int32: case Array::Contiguous: { JSValueOperand value(this, child3); @@ -2796,8 +2875,15 @@ void SpeculativeJIT::compile(Node& node) if (!m_compileOkay) return; + + if (arrayMode.type() == Array::Int32 + && !isInt32Speculation(m_state.forNode(child3).m_type)) { + speculationCheck( + BadType, JSValueRegs(valueReg), child3, + m_jit.branch64(MacroAssembler::Below, valueReg, GPRInfo::tagTypeNumberRegister)); + } - if (Heap::isWriteBarrierEnabled()) { + if (arrayMode.type() == Array::Contiguous && Heap::isWriteBarrierEnabled()) { GPRTemporary scratch(this); writeBarrier(baseReg, value.gpr(), child3, WriteBarrierForPropertyAccess, scratch.gpr()); } @@ -2857,6 +2943,11 @@ void SpeculativeJIT::compile(Node& node) break; } + case Array::Double: { + compileDoublePutByVal(node, base, property); + break; + } + case Array::ArrayStorage: case Array::SlowPutArrayStorage: { JSValueOperand value(this, child3); @@ -3081,23 +3172,31 @@ void SpeculativeJIT::compile(Node& node) ASSERT(node.arrayMode().isJSArray()); SpeculateCellOperand base(this, node.child1()); - JSValueOperand value(this, node.child2()); GPRTemporary storageLength(this); GPRReg baseGPR = base.gpr(); - GPRReg valueGPR = value.gpr(); GPRReg storageLengthGPR = storageLength.gpr(); - if (Heap::isWriteBarrierEnabled()) { - GPRTemporary scratch(this); - writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); - } - StorageOperand storage(this, node.child3()); GPRReg storageGPR = storage.gpr(); switch (node.arrayMode().type()) { + case Array::Int32: case Array::Contiguous: { + JSValueOperand value(this, node.child2()); + GPRReg valueGPR = value.gpr(); + + if (node.arrayMode().type() == Array::Int32 && !isInt32Speculation(m_state.forNode(node.child2()).m_type)) { + speculationCheck( + BadType, JSValueRegs(valueGPR), node.child2(), + m_jit.branch64(MacroAssembler::Below, valueGPR, GPRInfo::tagTypeNumberRegister)); + } + + if (node.arrayMode().type() != Array::Int32 && Heap::isWriteBarrierEnabled()) { + GPRTemporary scratch(this); + writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); + } + 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)); @@ -3114,7 +3213,43 @@ void SpeculativeJIT::compile(Node& node) break; } + case Array::Double: { + SpeculateDoubleOperand value(this, node.child2()); + FPRReg valueFPR = value.fpr(); + + if (!isRealNumberSpeculation(m_state.forNode(node.child2()).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, valueFPR, valueFPR)); + } + + 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.storeDouble(valueFPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight)); + m_jit.add32(TrustedImm32(1), storageLengthGPR); + m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); + m_jit.or64(GPRInfo::tagTypeNumberRegister, storageLengthGPR); + + addSlowPathGenerator( + slowPathCall( + slowPath, this, operationArrayPushDouble, NoResult, storageLengthGPR, + valueFPR, baseGPR)); + + jsValueResult(storageLengthGPR, m_compileIndex); + break; + } + case Array::ArrayStorage: { + JSValueOperand value(this, node.child2()); + GPRReg valueGPR = value.gpr(); + + if (Heap::isWriteBarrierEnabled()) { + GPRTemporary scratch(this); + writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); + } + m_jit.load32(MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset()), storageLengthGPR); // Refuse to handle bizarre lengths. @@ -3152,13 +3287,17 @@ void SpeculativeJIT::compile(Node& node) StorageOperand storage(this, node.child2()); GPRTemporary value(this); GPRTemporary storageLength(this); + FPRTemporary temp(this); // This is kind of lame, since we don't always need it. I'm relying on the fact that we don't have FPR pressure, especially in code that uses pop(). GPRReg baseGPR = base.gpr(); GPRReg storageGPR = storage.gpr(); GPRReg valueGPR = value.gpr(); GPRReg storageLengthGPR = storageLength.gpr(); + FPRReg tempFPR = temp.fpr(); switch (node.arrayMode().type()) { + case Array::Int32: + case Array::Double: case Array::Contiguous: { m_jit.load32( MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); @@ -3167,14 +3306,27 @@ void SpeculativeJIT::compile(Node& node) m_jit.sub32(TrustedImm32(1), storageLengthGPR); m_jit.store32( storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); - m_jit.load64( - MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight), - valueGPR); - // FIXME: This would not have to be here if changing the publicLength also zeroed the values between the old - // length and the new length. - m_jit.store64( + MacroAssembler::Jump slowCase; + if (node.arrayMode().type() == Array::Double) { + m_jit.loadDouble( + MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight), + tempFPR); + // FIXME: This would not have to be here if changing the publicLength also zeroed the values between the old + // length and the new length. + m_jit.store64( + MacroAssembler::TrustedImm64((int64_t)0), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight)); + slowCase = m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempFPR, tempFPR); + boxDouble(tempFPR, valueGPR); + } else { + m_jit.load64( + MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight), + valueGPR); + // FIXME: This would not have to be here if changing the publicLength also zeroed the values between the old + // length and the new length. + m_jit.store64( MacroAssembler::TrustedImm64((int64_t)0), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight)); - MacroAssembler::Jump slowCase = m_jit.branchTest64(MacroAssembler::Zero, valueGPR); + slowCase = m_jit.branchTest64(MacroAssembler::Zero, valueGPR); + } addSlowPathGenerator( slowPathMove( @@ -3184,6 +3336,7 @@ void SpeculativeJIT::compile(Node& node) slowPathCall( slowCase, this, operationArrayPopAndRecoverLength, valueGPR, baseGPR)); + // We can't know for sure that the result is an int because of the slow paths. :-/ jsValueResult(valueGPR, m_compileIndex); break; } @@ -3338,10 +3491,16 @@ void SpeculativeJIT::compile(Node& node) case NewArray: { JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin); - if (!globalObject->isHavingABadTime()) { + if (!globalObject->isHavingABadTime() && !hasArrayStorage(node.indexingType())) { globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint()); - ASSERT(hasContiguous(globalObject->arrayStructure()->indexingType())); + Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()); + ASSERT(structure->indexingType() == node.indexingType()); + ASSERT( + hasUndecided(structure->indexingType()) + || hasInt32(structure->indexingType()) + || hasDouble(structure->indexingType()) + || hasContiguous(structure->indexingType())); unsigned numElements = node.numChildren(); @@ -3351,15 +3510,50 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); GPRReg storageGPR = storage.gpr(); - emitAllocateJSArray(globalObject->arrayStructure(), resultGPR, storageGPR, numElements); + emitAllocateJSArray(structure, resultGPR, storageGPR, numElements); // At this point, one way or another, resultGPR and storageGPR have pointers to // the JSArray and the Butterfly, respectively. + ASSERT(!hasUndecided(structure->indexingType()) || !node.numChildren()); + for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) { - JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]); - GPRReg opGPR = operand.gpr(); - m_jit.store64(opGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx)); + Edge use = m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]; + switch (node.indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + CRASH(); + break; + case ALL_DOUBLE_INDEXING_TYPES: { + SpeculateDoubleOperand operand(this, use); + FPRReg opFPR = operand.fpr(); + if (!isRealNumberSpeculation(m_state.forNode(use).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR)); + } + + m_jit.storeDouble(opFPR, MacroAssembler::Address(storageGPR, sizeof(double) * operandIdx)); + break; + } + case ALL_INT32_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: { + JSValueOperand operand(this, use); + GPRReg opGPR = operand.gpr(); + if (hasInt32(node.indexingType()) && !isInt32Speculation(m_state.forNode(use).m_type)) { + speculationCheck( + BadType, JSValueRegs(opGPR), use.index(), + m_jit.branch64(MacroAssembler::Below, opGPR, GPRInfo::tagTypeNumberRegister)); + } + m_jit.store64(opGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx)); + break; + } + default: + CRASH(); + break; + } } // Yuck, we should *really* have a way of also returning the storageGPR. But @@ -3376,7 +3570,7 @@ void SpeculativeJIT::compile(Node& node) if (!node.numChildren()) { flushRegisters(); GPRResult result(this); - callOperation(operationNewEmptyArray, result.gpr(), globalObject->arrayStructure()); + callOperation(operationNewEmptyArray, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())); cellResult(result.gpr(), m_compileIndex); break; } @@ -3386,11 +3580,65 @@ void SpeculativeJIT::compile(Node& node) EncodedJSValue* buffer = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0; for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) { - JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]); - GPRReg opGPR = operand.gpr(); - operand.use(); - - m_jit.store64(opGPR, buffer + operandIdx); + // Need to perform the speculations that this node promises to perform. If we're + // emitting code here and the indexing type is not array storage then there is + // probably something hilarious going on and we're already failing at all the + // things, but at least we're going to be sound. + Edge use = m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]; + switch (node.indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + CRASH(); + break; + case ALL_DOUBLE_INDEXING_TYPES: { + SpeculateDoubleOperand operand(this, use); + GPRTemporary scratch(this); + FPRReg opFPR = operand.fpr(); + GPRReg scratchGPR = scratch.gpr(); + if (!isRealNumberSpeculation(m_state.forNode(use).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR)); + } + + m_jit.boxDouble(opFPR, scratchGPR); + m_jit.store64(scratchGPR, buffer + operandIdx); + break; + } + case ALL_INT32_INDEXING_TYPES: { + JSValueOperand operand(this, use); + GPRReg opGPR = operand.gpr(); + if (hasInt32(node.indexingType()) && !isInt32Speculation(m_state.forNode(use).m_type)) { + speculationCheck( + BadType, JSValueRegs(opGPR), use.index(), + m_jit.branch64(MacroAssembler::Below, opGPR, GPRInfo::tagTypeNumberRegister)); + } + m_jit.store64(opGPR, buffer + operandIdx); + break; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + JSValueOperand operand(this, use); + GPRReg opGPR = operand.gpr(); + m_jit.store64(opGPR, buffer + operandIdx); + operand.use(); + break; + } + default: + CRASH(); + break; + } + } + + switch (node.indexingType()) { + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + useChildren(node); + break; + default: + break; } flushRegisters(); @@ -3406,7 +3654,7 @@ void SpeculativeJIT::compile(Node& node) GPRResult result(this); callOperation( - operationNewArray, result.gpr(), globalObject->arrayStructure(), + operationNewArray, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), static_cast<void*>(buffer), node.numChildren()); if (scratchSize) { @@ -3422,18 +3670,26 @@ void SpeculativeJIT::compile(Node& node) case NewArrayWithSize: { JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin); - if (!globalObject->isHavingABadTime()) { + if (!globalObject->isHavingABadTime() && !hasArrayStorage(node.indexingType())) { globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint()); SpeculateStrictInt32Operand size(this, node.child1()); GPRTemporary result(this); GPRTemporary storage(this); GPRTemporary scratch(this); + GPRTemporary scratch2; GPRReg sizeGPR = size.gpr(); GPRReg resultGPR = result.gpr(); GPRReg storageGPR = storage.gpr(); GPRReg scratchGPR = scratch.gpr(); + GPRReg scratch2GPR = InvalidGPRReg; + + if (hasDouble(node.indexingType())) { + GPRTemporary realScratch2(this, size); + scratch2.adopt(realScratch2); + scratch2GPR = scratch2.gpr(); + } MacroAssembler::JumpList slowCases; slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX))); @@ -3446,17 +3702,28 @@ void SpeculativeJIT::compile(Node& node) emitAllocateBasicStorage(resultGPR, storageGPR)); m_jit.subPtr(scratchGPR, storageGPR); emitAllocateBasicJSObject<JSArray, MarkedBlock::None>( - TrustedImmPtr(globalObject->arrayStructure()), resultGPR, scratchGPR, + TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())), resultGPR, scratchGPR, storageGPR, sizeof(JSArray), slowCases); m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); + if (hasDouble(node.indexingType())) { + m_jit.move(TrustedImm64(bitwise_cast<int64_t>(QNaN)), scratchGPR); + m_jit.move(sizeGPR, scratch2GPR); + MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratch2GPR); + MacroAssembler::Label loop = m_jit.label(); + m_jit.sub32(TrustedImm32(1), scratch2GPR); + m_jit.store64(scratchGPR, MacroAssembler::BaseIndex(storageGPR, scratch2GPR, MacroAssembler::TimesEight)); + m_jit.branchTest32(MacroAssembler::NonZero, scratch2GPR).linkTo(loop, &m_jit); + done.link(&m_jit); + } + addSlowPathGenerator(adoptPtr( new CallArrayAllocatorWithVariableSizeSlowPathGenerator( slowCases, this, operationNewArrayWithSize, resultGPR, - globalObject->arrayStructure(), - globalObject->arrayStructureWithArrayStorage(), + globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), + globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage), sizeGPR))); cellResult(resultGPR, m_compileIndex); @@ -3470,10 +3737,10 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); GPRReg structureGPR = selectScratchGPR(sizeGPR); MacroAssembler::Jump bigLength = m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX)); - m_jit.move(TrustedImmPtr(globalObject->arrayStructure()), structureGPR); + m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())), structureGPR); MacroAssembler::Jump done = m_jit.jump(); bigLength.link(&m_jit); - m_jit.move(TrustedImmPtr(globalObject->arrayStructureWithArrayStorage()), structureGPR); + m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage)), structureGPR); done.link(&m_jit); callOperation(operationNewArrayWithSize, resultGPR, structureGPR, sizeGPR); cellResult(resultGPR, m_compileIndex); @@ -3520,7 +3787,8 @@ void SpeculativeJIT::compile(Node& node) case NewArrayBuffer: { JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin); - if (!globalObject->isHavingABadTime()) { + IndexingType indexingType = node.indexingType(); + if (!globalObject->isHavingABadTime() && !hasArrayStorage(indexingType)) { globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint()); unsigned numElements = node.numConstants(); @@ -3531,13 +3799,23 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); GPRReg storageGPR = storage.gpr(); - emitAllocateJSArray(globalObject->arrayStructure(), resultGPR, storageGPR, numElements); + emitAllocateJSArray(globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType), resultGPR, storageGPR, numElements); + ASSERT(indexingType & IsArray); JSValue* data = m_jit.codeBlock()->constantBuffer(node.startConstant()); - for (unsigned index = 0; index < node.numConstants(); ++index) { - m_jit.store64( - Imm64(JSValue::encode(data[index])), - MacroAssembler::Address(storageGPR, sizeof(JSValue) * index)); + if (indexingType == ArrayWithDouble) { + for (unsigned index = 0; index < node.numConstants(); ++index) { + double value = data[index].asNumber(); + m_jit.store64( + Imm64(bitwise_cast<int64_t>(value)), + MacroAssembler::Address(storageGPR, sizeof(double) * index)); + } + } else { + for (unsigned index = 0; index < node.numConstants(); ++index) { + m_jit.store64( + Imm64(JSValue::encode(data[index])), + MacroAssembler::Address(storageGPR, sizeof(JSValue) * index)); + } } cellResult(resultGPR, m_compileIndex); @@ -3547,7 +3825,7 @@ void SpeculativeJIT::compile(Node& node) flushRegisters(); GPRResult result(this); - callOperation(operationNewArrayBuffer, result.gpr(), globalObject->arrayStructure(), node.startConstant(), node.numConstants()); + callOperation(operationNewArrayBuffer, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), node.startConstant(), node.numConstants()); cellResult(result.gpr(), m_compileIndex); break; @@ -3645,6 +3923,12 @@ void SpeculativeJIT::compile(Node& node) cellResult(resultGPR, m_compileIndex); break; } + + case InheritorIDWatchpoint: { + jsCast<JSFunction*>(node.function())->addInheritorIDWatchpoint(speculationWatchpoint()); + noResult(m_compileIndex); + break; + } case NewObject: { GPRTemporary result(this); @@ -3655,9 +3939,9 @@ void SpeculativeJIT::compile(Node& node) MacroAssembler::JumpList slowPath; - emitAllocateJSFinalObject(MacroAssembler::TrustedImmPtr(m_jit.globalObjectFor(node.codeOrigin)->emptyObjectStructure()), resultGPR, scratchGPR, slowPath); + emitAllocateJSFinalObject(MacroAssembler::TrustedImmPtr(node.structure()), resultGPR, scratchGPR, slowPath); - addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR)); + addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR, node.structure())); cellResult(resultGPR, m_compileIndex); break; @@ -3813,7 +4097,7 @@ void SpeculativeJIT::compile(Node& node) case CheckFunction: { SpeculateCellOperand function(this, node.child1()); - speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, function.gpr(), node.function())); + speculationCheck(BadCache, JSValueRegs(function.gpr()), node.child1(), m_jit.branchWeakPtr(JITCompiler::NotEqual, function.gpr(), node.function())); noResult(m_compileIndex); break; } diff --git a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp index 22b9395b5..9d6060d60 100644 --- a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp @@ -167,7 +167,7 @@ public: if (iter == m_map.end()) continue; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Zeroing the structure to hoist for %s because the ratio is %lf.\n", + dataLogF("Zeroing the structure to hoist for %s because the ratio is %lf.\n", m_graph.nameOfVariableAccessData(variable), variable->voteRatio()); #endif iter->value.m_structure = 0; @@ -200,7 +200,7 @@ public: JSValue value = m_graph.m_mustHandleValues[i]; if (!value || !value.isCell()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Zeroing the structure to hoist for %s because the OSR entry value is not a cell: %s.\n", + dataLogF("Zeroing the structure to hoist for %s because the OSR entry value is not a cell: %s.\n", m_graph.nameOfVariableAccessData(variable), value.description()); #endif iter->value.m_structure = 0; @@ -208,7 +208,7 @@ public: } if (value.asCell()->structure() != iter->value.m_structure) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Zeroing the structure to hoist for %s because the OSR entry value has structure %p and we wanted %p.\n", + dataLogF("Zeroing the structure to hoist for %s because the OSR entry value has structure %p and we wanted %p.\n", m_graph.nameOfVariableAccessData(variable), value.asCell()->structure(), iter->value.m_structure); #endif iter->value.m_structure = 0; @@ -223,10 +223,10 @@ public: for (HashMap<VariableAccessData*, CheckData>::iterator it = m_map.begin(); it != m_map.end(); ++it) { if (!it->value.m_structure) { - dataLog("Not hoisting checks for %s because of heuristics.\n", m_graph.nameOfVariableAccessData(it->key)); + dataLogF("Not hoisting checks for %s because of heuristics.\n", m_graph.nameOfVariableAccessData(it->key)); continue; } - dataLog("Hoisting checks for %s\n", m_graph.nameOfVariableAccessData(it->key)); + dataLogF("Hoisting checks for %s\n", m_graph.nameOfVariableAccessData(it->key)); } #endif // DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) diff --git a/Source/JavaScriptCore/dfg/DFGThunks.cpp b/Source/JavaScriptCore/dfg/DFGThunks.cpp index 74d1967a8..ac0b45f60 100644 --- a/Source/JavaScriptCore/dfg/DFGThunks.cpp +++ b/Source/JavaScriptCore/dfg/DFGThunks.cpp @@ -213,6 +213,18 @@ MacroAssemblerCodeRef linkConstructThunkGenerator(JSGlobalData* globalData) return linkForThunkGenerator(globalData, CodeForConstruct); } +// For closure optimizations, we only include calls, since if you're using closures for +// object construction then you're going to lose big time anyway. +MacroAssemblerCodeRef linkClosureCallThunkGenerator(JSGlobalData* globalData) +{ + CCallHelpers jit(globalData); + + slowPathFor(jit, globalData, operationLinkClosureCall); + + LinkBuffer patchBuffer(*globalData, &jit, GLOBAL_THUNK_ID); + return FINALIZE_CODE(patchBuffer, ("DFG link closure call slow path thunk")); +} + static MacroAssemblerCodeRef virtualForThunkGenerator( JSGlobalData* globalData, CodeSpecializationKind kind) { diff --git a/Source/JavaScriptCore/dfg/DFGThunks.h b/Source/JavaScriptCore/dfg/DFGThunks.h index 11a06d107..c97e3bfb6 100644 --- a/Source/JavaScriptCore/dfg/DFGThunks.h +++ b/Source/JavaScriptCore/dfg/DFGThunks.h @@ -45,6 +45,8 @@ MacroAssemblerCodeRef throwExceptionFromCallSlowPathGenerator(JSGlobalData*); MacroAssemblerCodeRef linkCallThunkGenerator(JSGlobalData*); MacroAssemblerCodeRef linkConstructThunkGenerator(JSGlobalData*); +MacroAssemblerCodeRef linkClosureCallThunkGenerator(JSGlobalData*); + MacroAssemblerCodeRef virtualCallThunkGenerator(JSGlobalData*); MacroAssemblerCodeRef virtualConstructThunkGenerator(JSGlobalData*); diff --git a/Source/JavaScriptCore/dfg/DFGValidate.cpp b/Source/JavaScriptCore/dfg/DFGValidate.cpp index 2b26123d8..274b544b5 100644 --- a/Source/JavaScriptCore/dfg/DFGValidate.cpp +++ b/Source/JavaScriptCore/dfg/DFGValidate.cpp @@ -45,9 +45,9 @@ public: #define VALIDATE(context, assertion) do { \ if (!(assertion)) { \ - dataLog("\n\n\nAt "); \ + dataLogF("\n\n\nAt "); \ reportValidationContext context; \ - dataLog(": validation %s (%s:%d) failed.\n", #assertion, __FILE__, __LINE__); \ + dataLogF(": validation %s (%s:%d) failed.\n", #assertion, __FILE__, __LINE__); \ dumpGraphIfAppropriate(); \ WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion); \ CRASH(); \ @@ -56,13 +56,13 @@ public: #define V_EQUAL(context, left, right) do { \ if (left != right) { \ - dataLog("\n\n\nAt "); \ + dataLogF("\n\n\nAt "); \ reportValidationContext context; \ - dataLog(": validation (%s = ", #left); \ + dataLogF(": validation (%s = ", #left); \ dumpData(left); \ - dataLog(") == (%s = ", #right); \ + dataLogF(") == (%s = ", #right); \ dumpData(right); \ - dataLog(") (%s:%d) failed.\n", __FILE__, __LINE__); \ + dataLogF(") (%s:%d) failed.\n", __FILE__, __LINE__); \ dumpGraphIfAppropriate(); \ WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #left " == " #right); \ CRASH(); \ @@ -290,60 +290,60 @@ private: void reportValidationContext(NodeIndex nodeIndex) { - dataLog("@%u", nodeIndex); + dataLogF("@%u", nodeIndex); } enum BlockTag { Block }; void reportValidationContext(BlockTag, BlockIndex blockIndex) { - dataLog("Block #%u", blockIndex); + dataLogF("Block #%u", blockIndex); } void reportValidationContext(NodeIndex nodeIndex, Edge edge) { - dataLog("@%u -> %s@%u", nodeIndex, useKindToString(edge.useKind()), edge.index()); + dataLogF("@%u -> %s@%u", nodeIndex, useKindToString(edge.useKind()), edge.index()); } void reportValidationContext( VirtualRegister local, BlockIndex sourceBlockIndex, BlockTag, BlockIndex destinationBlockIndex) { - dataLog("r%d in Block #%u -> #%u", local, sourceBlockIndex, destinationBlockIndex); + dataLogF("r%d in Block #%u -> #%u", local, sourceBlockIndex, destinationBlockIndex); } void reportValidationContext( VirtualRegister local, BlockIndex sourceBlockIndex, NodeIndex prevNodeIndex) { - dataLog("@%u for r%d in Block #%u", prevNodeIndex, local, sourceBlockIndex); + dataLogF("@%u for r%d in Block #%u", prevNodeIndex, local, sourceBlockIndex); } void reportValidationContext( NodeIndex nodeIndex, BlockIndex blockIndex) { - dataLog("@%u in Block #%u", nodeIndex, blockIndex); + dataLogF("@%u in Block #%u", nodeIndex, blockIndex); } void reportValidationContext( NodeIndex nodeIndex, NodeIndex nodeIndex2, BlockIndex blockIndex) { - dataLog("@%u and @%u in Block #%u", nodeIndex, nodeIndex2, blockIndex); + dataLogF("@%u and @%u in Block #%u", nodeIndex, nodeIndex2, blockIndex); } void reportValidationContext( NodeIndex nodeIndex, BlockIndex blockIndex, NodeIndex expectedNodeIndex, Edge incomingEdge) { - dataLog("@%u in Block #%u, searching for @%u from @%u", nodeIndex, blockIndex, expectedNodeIndex, incomingEdge.index()); + dataLogF("@%u in Block #%u, searching for @%u from @%u", nodeIndex, blockIndex, expectedNodeIndex, incomingEdge.index()); } void dumpData(unsigned value) { - dataLog("%u", value); + dataLogF("%u", value); } void dumpGraphIfAppropriate() { if (m_graphDumpMode == DontDumpGraph) return; - dataLog("Graph at time of failure:\n"); + dataLogF("Graph at time of failure:\n"); m_graph.dump(); } }; diff --git a/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp b/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp index fa36ccdb5..7fa109b62 100644 --- a/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp +++ b/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp @@ -36,9 +36,9 @@ namespace JSC { namespace DFG { void VariableEventStream::logEvent(const VariableEvent& event) { - dataLog("seq#%u:", static_cast<unsigned>(size())); + dataLogF("seq#%u:", static_cast<unsigned>(size())); event.dump(WTF::dataFile()); - dataLog(" "); + dataLogF(" "); } struct MinifiedGenerationInfo { @@ -103,7 +103,7 @@ void VariableEventStream::reconstruct( startIndex--; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Computing OSR exit recoveries starting at seq#%u.\n", startIndex); + dataLogF("Computing OSR exit recoveries starting at seq#%u.\n", startIndex); #endif // Step 2: Create a mock-up of the DFG's state and execute the events. diff --git a/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp b/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp index 86b33835d..eb3232e69 100644 --- a/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp @@ -43,9 +43,9 @@ public: bool run() { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Preserved vars: "); + dataLogF("Preserved vars: "); m_graph.m_preservedVars.dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif ScoreBoard scoreBoard(m_graph, m_graph.m_preservedVars); scoreBoard.assertClear(); @@ -62,8 +62,8 @@ public: NodeIndex nodeIndex = block->at(indexInBlock); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) if (needsNewLine) - dataLog("\n"); - dataLog(" @%u:", nodeIndex); + dataLogF("\n"); + dataLogF(" @%u:", nodeIndex); needsNewLine = true; #endif Node& node = m_graph[nodeIndex]; @@ -92,7 +92,7 @@ public: VirtualRegister virtualRegister = scoreBoard.allocate(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Assigning virtual register %u to node %u.", + dataLogF(" Assigning virtual register %u to node %u.", virtualRegister, nodeIndex); #endif node.setVirtualRegister(virtualRegister); @@ -105,7 +105,7 @@ public: } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) if (needsNewLine) - dataLog("\n"); + dataLogF("\n"); #endif // 'm_numCalleeRegisters' is the number of locals and temporaries allocated @@ -123,7 +123,7 @@ public: if ((unsigned)codeBlock()->m_numCalleeRegisters < calleeRegisters) codeBlock()->m_numCalleeRegisters = calleeRegisters; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Num callee registers: %u\n", calleeRegisters); + dataLogF("Num callee registers: %u\n", calleeRegisters); #endif return true; diff --git a/Source/JavaScriptCore/disassembler/Disassembler.cpp b/Source/JavaScriptCore/disassembler/Disassembler.cpp new file mode 100644 index 000000000..84bf2ec17 --- /dev/null +++ b/Source/JavaScriptCore/disassembler/Disassembler.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Disassembler.h" + +#include "MacroAssemblerCodeRef.h" +#include <wtf/DataLog.h> + +namespace JSC { + +void disassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, FILE* out) +{ + if (tryToDisassemble(codePtr, size, prefix, out)) + return; + + fprintf(out, "%sdisassembly not available for range %p...%p\n", prefix, codePtr.executableAddress(), static_cast<char*>(codePtr.executableAddress()) + size); +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/disassembler/Disassembler.h b/Source/JavaScriptCore/disassembler/Disassembler.h index 7d7400ac8..b87f3d33a 100644 --- a/Source/JavaScriptCore/disassembler/Disassembler.h +++ b/Source/JavaScriptCore/disassembler/Disassembler.h @@ -43,6 +43,10 @@ inline bool tryToDisassemble(const MacroAssemblerCodePtr&, size_t, const char*, } #endif +// Prints either the disassembly, or a line of text indicating that disassembly failed and +// the range of machine code addresses. +void disassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, FILE* out); + } // namespace JSC #endif // Disassembler_h diff --git a/Source/JavaScriptCore/heap/BlockAllocator.cpp b/Source/JavaScriptCore/heap/BlockAllocator.cpp index daba93805..2d7b57f9a 100644 --- a/Source/JavaScriptCore/heap/BlockAllocator.cpp +++ b/Source/JavaScriptCore/heap/BlockAllocator.cpp @@ -36,7 +36,7 @@ namespace JSC { BlockAllocator::BlockAllocator() : m_copiedRegionSet(CopiedBlock::blockSize) , m_markedRegionSet(MarkedBlock::blockSize) - , m_weakRegionSet(WeakBlock::blockSize) + , m_weakAndMarkStackRegionSet(WeakBlock::blockSize) , m_numberOfEmptyRegions(0) , m_isCurrentlyAllocating(false) , m_blockFreeingThreadShouldQuit(false) diff --git a/Source/JavaScriptCore/heap/BlockAllocator.h b/Source/JavaScriptCore/heap/BlockAllocator.h index f8ce39530..75c59b783 100644 --- a/Source/JavaScriptCore/heap/BlockAllocator.h +++ b/Source/JavaScriptCore/heap/BlockAllocator.h @@ -37,6 +37,7 @@ namespace JSC { class BlockAllocator; class CopiedBlock; +class MarkStackSegment; class MarkedBlock; class Region; class WeakBlock; @@ -185,7 +186,8 @@ private: RegionSet m_copiedRegionSet; RegionSet m_markedRegionSet; - RegionSet m_weakRegionSet; + // WeakBlocks and MarkStackSegments use the same RegionSet since they're the same size. + RegionSet m_weakAndMarkStackRegionSet; DoublyLinkedList<Region> m_emptyRegions; size_t m_numberOfEmptyRegions; @@ -315,7 +317,13 @@ inline BlockAllocator::RegionSet& BlockAllocator::regionSetFor<MarkedBlock>() template <> inline BlockAllocator::RegionSet& BlockAllocator::regionSetFor<WeakBlock>() { - return m_weakRegionSet; + return m_weakAndMarkStackRegionSet; +} + +template <> +inline BlockAllocator::RegionSet& BlockAllocator::regionSetFor<MarkStackSegment>() +{ + return m_weakAndMarkStackRegionSet; } template <> @@ -333,7 +341,13 @@ inline BlockAllocator::RegionSet& BlockAllocator::regionSetFor<HeapBlock<MarkedB template <> inline BlockAllocator::RegionSet& BlockAllocator::regionSetFor<HeapBlock<WeakBlock> >() { - return m_weakRegionSet; + return m_weakAndMarkStackRegionSet; +} + +template <> +inline BlockAllocator::RegionSet& BlockAllocator::regionSetFor<HeapBlock<MarkStackSegment> >() +{ + return m_weakAndMarkStackRegionSet; } template <typename T> diff --git a/Source/JavaScriptCore/heap/ConservativeRoots.cpp b/Source/JavaScriptCore/heap/ConservativeRoots.cpp index 7fe22dfff..752ce2775 100644 --- a/Source/JavaScriptCore/heap/ConservativeRoots.cpp +++ b/Source/JavaScriptCore/heap/ConservativeRoots.cpp @@ -26,9 +26,9 @@ #include "config.h" #include "ConservativeRoots.h" -#include "CopiedSpace.h" -#include "CopiedSpaceInlineMethods.h" #include "CodeBlock.h" +#include "CopiedSpace.h" +#include "CopiedSpaceInlines.h" #include "DFGCodeBlocks.h" #include "JSCell.h" #include "JSObject.h" diff --git a/Source/JavaScriptCore/heap/CopiedBlock.h b/Source/JavaScriptCore/heap/CopiedBlock.h index af36f55df..83fdb08da 100644 --- a/Source/JavaScriptCore/heap/CopiedBlock.h +++ b/Source/JavaScriptCore/heap/CopiedBlock.h @@ -29,7 +29,7 @@ #include "BlockAllocator.h" #include "HeapBlock.h" #include "JSValue.h" -#include "JSValueInlineMethods.h" +#include "JSValueInlines.h" #include "Options.h" #include <wtf/Atomics.h> diff --git a/Source/JavaScriptCore/heap/CopiedSpace.cpp b/Source/JavaScriptCore/heap/CopiedSpace.cpp index c228f9460..e4141c1d7 100644 --- a/Source/JavaScriptCore/heap/CopiedSpace.cpp +++ b/Source/JavaScriptCore/heap/CopiedSpace.cpp @@ -26,7 +26,7 @@ #include "config.h" #include "CopiedSpace.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "GCActivityCallback.h" #include "Options.h" diff --git a/Source/JavaScriptCore/heap/CopiedSpaceInlineMethods.h b/Source/JavaScriptCore/heap/CopiedSpaceInlines.h index c244015e7..9d222f549 100644 --- a/Source/JavaScriptCore/heap/CopiedSpaceInlineMethods.h +++ b/Source/JavaScriptCore/heap/CopiedSpaceInlines.h @@ -23,8 +23,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CopiedSpaceInlineMethods_h -#define CopiedSpaceInlineMethods_h +#ifndef CopiedSpaceInlines_h +#define CopiedSpaceInlines_h #include "CopiedBlock.h" #include "CopiedSpace.h" @@ -182,4 +182,5 @@ inline CopiedBlock* CopiedSpace::blockFor(void* ptr) } // namespace JSC -#endif +#endif // CopiedSpaceInlines_h + diff --git a/Source/JavaScriptCore/heap/CopyVisitor.cpp b/Source/JavaScriptCore/heap/CopyVisitor.cpp index ae826f0d2..22ab57882 100644 --- a/Source/JavaScriptCore/heap/CopyVisitor.cpp +++ b/Source/JavaScriptCore/heap/CopyVisitor.cpp @@ -26,7 +26,7 @@ #include "config.h" #include "CopyVisitor.h" -#include "CopyVisitorInlineMethods.h" +#include "CopyVisitorInlines.h" #include "GCThreadSharedData.h" #include "JSCell.h" #include "JSObject.h" diff --git a/Source/JavaScriptCore/heap/CopyVisitorInlineMethods.h b/Source/JavaScriptCore/heap/CopyVisitorInlines.h index eb7bd2e82..bd7879429 100644 --- a/Source/JavaScriptCore/heap/CopyVisitorInlineMethods.h +++ b/Source/JavaScriptCore/heap/CopyVisitorInlines.h @@ -23,8 +23,8 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CopyVisitorInlineMethods_h -#define CopyVisitorInlineMethods_h +#ifndef CopyVisitorInlines_h +#define CopyVisitorInlines_h #include "ClassInfo.h" #include "CopyVisitor.h" @@ -116,4 +116,5 @@ inline void CopyVisitor::didCopy(void* ptr, size_t bytes) } // namespace JSC -#endif +#endif // CopyVisitorInlines_h + diff --git a/Source/JavaScriptCore/heap/GCThread.cpp b/Source/JavaScriptCore/heap/GCThread.cpp index ce3bbedc9..7caa7d588 100644 --- a/Source/JavaScriptCore/heap/GCThread.cpp +++ b/Source/JavaScriptCore/heap/GCThread.cpp @@ -27,7 +27,7 @@ #include "GCThread.h" #include "CopyVisitor.h" -#include "CopyVisitorInlineMethods.h" +#include "CopyVisitorInlines.h" #include "GCThreadSharedData.h" #include "SlotVisitor.h" #include <wtf/MainThread.h> diff --git a/Source/JavaScriptCore/heap/GCThreadSharedData.cpp b/Source/JavaScriptCore/heap/GCThreadSharedData.cpp index 446b41c2f..f513fafab 100644 --- a/Source/JavaScriptCore/heap/GCThreadSharedData.cpp +++ b/Source/JavaScriptCore/heap/GCThreadSharedData.cpp @@ -27,12 +27,12 @@ #include "GCThreadSharedData.h" #include "CopyVisitor.h" -#include "CopyVisitorInlineMethods.h" +#include "CopyVisitorInlines.h" #include "GCThread.h" #include "JSGlobalData.h" #include "MarkStack.h" #include "SlotVisitor.h" -#include "SlotVisitorInlineMethods.h" +#include "SlotVisitorInlines.h" namespace JSC { @@ -56,7 +56,7 @@ GCThreadSharedData::GCThreadSharedData(JSGlobalData* globalData) : m_globalData(globalData) , m_copiedSpace(&globalData->heap.m_storageSpace) , m_shouldHashConst(false) - , m_sharedMarkStack(m_segmentAllocator) + , m_sharedMarkStack(globalData->heap.blockAllocator()) , m_numberOfActiveParallelMarkers(0) , m_parallelMarkersShouldExit(false) , m_blocksToCopy(globalData->heap.m_blockSnapshot) @@ -110,7 +110,6 @@ void GCThreadSharedData::reset() ASSERT(m_sharedMarkStack.isEmpty()); #if ENABLE(PARALLEL_GC) - m_segmentAllocator.shrinkReserve(); m_opaqueRoots.clear(); #else ASSERT(m_opaqueRoots.isEmpty()); diff --git a/Source/JavaScriptCore/heap/GCThreadSharedData.h b/Source/JavaScriptCore/heap/GCThreadSharedData.h index f341afc04..b80cc5af2 100644 --- a/Source/JavaScriptCore/heap/GCThreadSharedData.h +++ b/Source/JavaScriptCore/heap/GCThreadSharedData.h @@ -80,8 +80,6 @@ private: JSGlobalData* m_globalData; CopiedSpace* m_copiedSpace; - MarkStackSegmentAllocator m_segmentAllocator; - bool m_shouldHashConst; Vector<GCThread*> m_gcThreads; diff --git a/Source/JavaScriptCore/heap/HandleStack.cpp b/Source/JavaScriptCore/heap/HandleStack.cpp index 42eb326a5..a5653c748 100644 --- a/Source/JavaScriptCore/heap/HandleStack.cpp +++ b/Source/JavaScriptCore/heap/HandleStack.cpp @@ -27,8 +27,8 @@ #include "HandleStack.h" #include "HeapRootVisitor.h" -#include "JSValueInlineMethods.h" #include "JSObject.h" +#include "JSValueInlines.h" namespace JSC { diff --git a/Source/JavaScriptCore/heap/Heap.cpp b/Source/JavaScriptCore/heap/Heap.cpp index c455fc2b1..9ff318b08 100644 --- a/Source/JavaScriptCore/heap/Heap.cpp +++ b/Source/JavaScriptCore/heap/Heap.cpp @@ -24,8 +24,8 @@ #include "CodeBlock.h" #include "ConservativeRoots.h" #include "CopiedSpace.h" -#include "CopiedSpaceInlineMethods.h" -#include "CopyVisitorInlineMethods.h" +#include "CopiedSpaceInlines.h" +#include "CopyVisitorInlines.h" #include "GCActivityCallback.h" #include "HeapRootVisitor.h" #include "HeapStatistics.h" @@ -76,7 +76,7 @@ struct GCTimer { } ~GCTimer() { - dataLog("%s: %.2lfms (avg. %.2lf, min. %.2lf, max. %.2lf)\n", m_name, m_time * 1000, m_time * 1000 / m_count, m_min*1000, m_max*1000); + dataLogF("%s: %.2lfms (avg. %.2lf, min. %.2lf, max. %.2lf)\n", m_name, m_time * 1000, m_time * 1000 / m_count, m_min*1000, m_max*1000); } double m_time; double m_min; @@ -126,7 +126,7 @@ struct GCCounter { } ~GCCounter() { - dataLog("%s: %zu values (avg. %zu, min. %zu, max. %zu)\n", m_name, m_total, m_total / m_count, m_min, m_max); + dataLogF("%s: %zu values (avg. %zu, min. %zu, max. %zu)\n", m_name, m_total, m_total / m_count, m_min, m_max); } const char* m_name; size_t m_count; diff --git a/Source/JavaScriptCore/heap/Heap.h b/Source/JavaScriptCore/heap/Heap.h index d0d03959b..90c9f2ab1 100644 --- a/Source/JavaScriptCore/heap/Heap.h +++ b/Source/JavaScriptCore/heap/Heap.h @@ -174,6 +174,8 @@ namespace JSC { bool isPagedOut(double deadline); void didStartVMShutdown(); + + const JITStubRoutineSet& jitStubRoutines() { return m_jitStubRoutines; } private: friend class CodeBlock; diff --git a/Source/JavaScriptCore/heap/HeapRootVisitor.h b/Source/JavaScriptCore/heap/HeapRootVisitor.h index 9849d7c39..5b11a5ead 100644 --- a/Source/JavaScriptCore/heap/HeapRootVisitor.h +++ b/Source/JavaScriptCore/heap/HeapRootVisitor.h @@ -27,7 +27,7 @@ #define HeapRootVisitor_h #include "SlotVisitor.h" -#include "SlotVisitorInlineMethods.h" +#include "SlotVisitorInlines.h" namespace JSC { diff --git a/Source/JavaScriptCore/heap/HeapStatistics.cpp b/Source/JavaScriptCore/heap/HeapStatistics.cpp index 8340bfa37..2b98fe711 100644 --- a/Source/JavaScriptCore/heap/HeapStatistics.cpp +++ b/Source/JavaScriptCore/heap/HeapStatistics.cpp @@ -41,8 +41,8 @@ namespace JSC { double HeapStatistics::s_startTime = 0.0; double HeapStatistics::s_endTime = 0.0; -Deque<double>* HeapStatistics::s_pauseTimeStarts = 0; -Deque<double>* HeapStatistics::s_pauseTimeEnds = 0; +Vector<double>* HeapStatistics::s_pauseTimeStarts = 0; +Vector<double>* HeapStatistics::s_pauseTimeEnds = 0; #if OS(UNIX) @@ -50,8 +50,8 @@ void HeapStatistics::initialize() { ASSERT(Options::recordGCPauseTimes()); s_startTime = WTF::monotonicallyIncreasingTime(); - s_pauseTimeStarts = new Deque<double>(); - s_pauseTimeEnds = new Deque<double>(); + s_pauseTimeStarts = new Vector<double>(); + s_pauseTimeEnds = new Vector<double>(); } void HeapStatistics::recordGCPauseTime(double start, double end) @@ -75,28 +75,28 @@ void HeapStatistics::logStatistics() #error "The HeapStatistics module is not supported on this platform." #endif if (!vmName || !suiteName || !benchmarkName) - dataLog("HeapStatistics: {\"max_rss\": %ld", usage.ru_maxrss); + dataLogF("HeapStatistics: {\"max_rss\": %ld", usage.ru_maxrss); else - dataLog("HeapStatistics: {\"max_rss\": %ld, \"vm_name\": \"%s\", \"suite_name\": \"%s\", \"benchmark_name\": \"%s\"", + dataLogF("HeapStatistics: {\"max_rss\": %ld, \"vm_name\": \"%s\", \"suite_name\": \"%s\", \"benchmark_name\": \"%s\"", usage.ru_maxrss, vmName, suiteName, benchmarkName); if (Options::recordGCPauseTimes()) { - dataLog(", \"pause_times\": ["); - Deque<double>::iterator startIt = s_pauseTimeStarts->begin(); - Deque<double>::iterator endIt = s_pauseTimeEnds->begin(); + dataLogF(", \"pause_times\": ["); + Vector<double>::iterator startIt = s_pauseTimeStarts->begin(); + Vector<double>::iterator endIt = s_pauseTimeEnds->begin(); if (startIt != s_pauseTimeStarts->end() && endIt != s_pauseTimeEnds->end()) { - dataLog("[%f, %f]", *startIt, *endIt); + dataLogF("[%f, %f]", *startIt, *endIt); ++startIt; ++endIt; } while (startIt != s_pauseTimeStarts->end() && endIt != s_pauseTimeEnds->end()) { - dataLog(", [%f, %f]", *startIt, *endIt); + dataLogF(", [%f, %f]", *startIt, *endIt); ++startIt; ++endIt; } - dataLog("], \"start_time\": %f, \"end_time\": %f", s_startTime, s_endTime); + dataLogF("], \"start_time\": %f, \"end_time\": %f", s_startTime, s_endTime); } - dataLog("}\n"); + dataLogF("}\n"); } void HeapStatistics::exitWithFailure() @@ -228,20 +228,20 @@ inline size_t StorageStatistics::storageCapacity() void HeapStatistics::showObjectStatistics(Heap* heap) { - dataLog("\n=== Heap Statistics: ===\n"); - dataLog("size: %ldkB\n", static_cast<long>(heap->m_sizeAfterLastCollect / KB)); - dataLog("capacity: %ldkB\n", static_cast<long>(heap->capacity() / KB)); - dataLog("pause time: %lfms\n\n", heap->m_lastGCLength); + dataLogF("\n=== Heap Statistics: ===\n"); + dataLogF("size: %ldkB\n", static_cast<long>(heap->m_sizeAfterLastCollect / KB)); + dataLogF("capacity: %ldkB\n", static_cast<long>(heap->capacity() / KB)); + dataLogF("pause time: %lfms\n\n", heap->m_lastGCLength); StorageStatistics storageStatistics; heap->m_objectSpace.forEachLiveCell(storageStatistics); - dataLog("wasted .property storage: %ldkB (%ld%%)\n", + dataLogF("wasted .property storage: %ldkB (%ld%%)\n", static_cast<long>( (storageStatistics.storageCapacity() - storageStatistics.storageSize()) / KB), static_cast<long>( (storageStatistics.storageCapacity() - storageStatistics.storageSize()) * 100 / storageStatistics.storageCapacity())); - dataLog("objects with out-of-line .property storage: %ld (%ld%%)\n", + dataLogF("objects with out-of-line .property storage: %ld (%ld%%)\n", static_cast<long>( storageStatistics.objectWithOutOfLineStorageCount()), static_cast<long>( diff --git a/Source/JavaScriptCore/heap/HeapStatistics.h b/Source/JavaScriptCore/heap/HeapStatistics.h index 0800f0c16..ce7a40a79 100644 --- a/Source/JavaScriptCore/heap/HeapStatistics.h +++ b/Source/JavaScriptCore/heap/HeapStatistics.h @@ -49,8 +49,8 @@ public: private: static void logStatistics(); - static Deque<double>* s_pauseTimeStarts; - static Deque<double>* s_pauseTimeEnds; + static Vector<double>* s_pauseTimeStarts; + static Vector<double>* s_pauseTimeEnds; static double s_startTime; static double s_endTime; }; diff --git a/Source/JavaScriptCore/heap/JITStubRoutineSet.h b/Source/JavaScriptCore/heap/JITStubRoutineSet.h index cb76ac8bd..2c2e9fa86 100644 --- a/Source/JavaScriptCore/heap/JITStubRoutineSet.h +++ b/Source/JavaScriptCore/heap/JITStubRoutineSet.h @@ -65,6 +65,9 @@ public: void traceMarkedStubRoutines(SlotVisitor&); + unsigned size() const { return m_listOfRoutines.size(); } + GCAwareJITStubRoutine* at(unsigned i) const { return m_listOfRoutines[i]; } + private: void markSlow(uintptr_t address); diff --git a/Source/JavaScriptCore/heap/MarkStack.cpp b/Source/JavaScriptCore/heap/MarkStack.cpp index 582439fd2..755a0ad50 100644 --- a/Source/JavaScriptCore/heap/MarkStack.cpp +++ b/Source/JavaScriptCore/heap/MarkStack.cpp @@ -25,18 +25,18 @@ #include "config.h" #include "MarkStack.h" -#include "MarkStackInlineMethods.h" +#include "MarkStackInlines.h" -#include "CopiedSpace.h" -#include "CopiedSpaceInlineMethods.h" #include "ConservativeRoots.h" +#include "CopiedSpace.h" +#include "CopiedSpaceInlines.h" #include "Heap.h" #include "Options.h" #include "JSArray.h" #include "JSCell.h" #include "JSObject.h" -#include "SlotVisitorInlineMethods.h" +#include "SlotVisitorInlines.h" #include "Structure.h" #include "WriteBarrier.h" #include <wtf/Atomics.h> @@ -45,84 +45,35 @@ namespace JSC { -MarkStackSegmentAllocator::MarkStackSegmentAllocator() - : m_nextFreeSegment(0) -{ - m_lock.Init(); -} - -MarkStackSegmentAllocator::~MarkStackSegmentAllocator() -{ - shrinkReserve(); -} - -MarkStackSegment* MarkStackSegmentAllocator::allocate() -{ - { - SpinLockHolder locker(&m_lock); - if (m_nextFreeSegment) { - MarkStackSegment* result = m_nextFreeSegment; - m_nextFreeSegment = result->m_previous; - return result; - } - } - - return static_cast<MarkStackSegment*>(OSAllocator::reserveAndCommit(Options::gcMarkStackSegmentSize())); -} - -void MarkStackSegmentAllocator::release(MarkStackSegment* segment) -{ - SpinLockHolder locker(&m_lock); - segment->m_previous = m_nextFreeSegment; - m_nextFreeSegment = segment; -} - -void MarkStackSegmentAllocator::shrinkReserve() -{ - MarkStackSegment* segments; - { - SpinLockHolder locker(&m_lock); - segments = m_nextFreeSegment; - m_nextFreeSegment = 0; - } - while (segments) { - MarkStackSegment* toFree = segments; - segments = segments->m_previous; - OSAllocator::decommitAndRelease(toFree, Options::gcMarkStackSegmentSize()); - } -} - -MarkStackArray::MarkStackArray(MarkStackSegmentAllocator& allocator) - : m_allocator(allocator) +MarkStackArray::MarkStackArray(BlockAllocator& blockAllocator) + : m_blockAllocator(blockAllocator) , m_segmentCapacity(MarkStackSegment::capacityFromSize(Options::gcMarkStackSegmentSize())) , m_top(0) - , m_numberOfPreviousSegments(0) + , m_numberOfSegments(0) { - m_topSegment = m_allocator.allocate(); -#if !ASSERT_DISABLED - m_topSegment->m_top = 0; -#endif - m_topSegment->m_previous = 0; + ASSERT(MarkStackSegment::blockSize == WeakBlock::blockSize); + m_segments.push(MarkStackSegment::create(m_blockAllocator.allocate<MarkStackSegment>())); + m_numberOfSegments++; } MarkStackArray::~MarkStackArray() { - ASSERT(!m_topSegment->m_previous); - m_allocator.release(m_topSegment); + ASSERT(m_numberOfSegments == 1 && m_segments.size() == 1); + m_blockAllocator.deallocate(MarkStackSegment::destroy(m_segments.removeHead())); } void MarkStackArray::expand() { - ASSERT(m_topSegment->m_top == m_segmentCapacity); + ASSERT(m_segments.head()->m_top == m_segmentCapacity); - m_numberOfPreviousSegments++; + MarkStackSegment* nextSegment = MarkStackSegment::create(m_blockAllocator.allocate<MarkStackSegment>()); + m_numberOfSegments++; - MarkStackSegment* nextSegment = m_allocator.allocate(); #if !ASSERT_DISABLED nextSegment->m_top = 0; #endif - nextSegment->m_previous = m_topSegment; - m_topSegment = nextSegment; + + m_segments.push(nextSegment); setTopForEmptySegment(); validatePrevious(); } @@ -132,14 +83,9 @@ bool MarkStackArray::refill() validatePrevious(); if (top()) return true; - MarkStackSegment* toFree = m_topSegment; - MarkStackSegment* previous = m_topSegment->m_previous; - if (!previous) - return false; - ASSERT(m_numberOfPreviousSegments); - m_numberOfPreviousSegments--; - m_topSegment = previous; - m_allocator.release(toFree); + m_blockAllocator.deallocate(MarkStackSegment::destroy(m_segments.removeHead())); + ASSERT(m_numberOfSegments > 1); + m_numberOfSegments--; setTopForFullSegment(); validatePrevious(); return true; @@ -153,7 +99,7 @@ void MarkStackArray::donateSomeCellsTo(MarkStackArray& other) ASSERT(m_segmentCapacity == other.m_segmentCapacity); - size_t segmentsToDonate = (m_numberOfPreviousSegments + 2 - 1) / 2; // Round up to donate 1 / 1 previous segments. + size_t segmentsToDonate = m_numberOfSegments / 2; // If we only have one segment (our head) we don't donate any segments. if (!segmentsToDonate) { size_t cellsToDonate = m_top / 2; // Round down to donate 0 / 1 cells. @@ -167,21 +113,23 @@ void MarkStackArray::donateSomeCellsTo(MarkStackArray& other) validatePrevious(); other.validatePrevious(); - MarkStackSegment* previous = m_topSegment->m_previous; + // Remove our head and the head of the other list before we start moving segments around. + // We'll add them back on once we're done donating. + MarkStackSegment* myHead = m_segments.removeHead(); + MarkStackSegment* otherHead = other.m_segments.removeHead(); + while (segmentsToDonate--) { - ASSERT(previous); - ASSERT(m_numberOfPreviousSegments); - - MarkStackSegment* current = previous; - previous = current->m_previous; - - current->m_previous = other.m_topSegment->m_previous; - other.m_topSegment->m_previous = current; - - m_numberOfPreviousSegments--; - other.m_numberOfPreviousSegments++; + MarkStackSegment* current = m_segments.removeHead(); + ASSERT(current); + ASSERT(m_numberOfSegments > 1); + other.m_segments.push(current); + m_numberOfSegments--; + other.m_numberOfSegments++; } - m_topSegment->m_previous = previous; + + // Put the original heads back in their places. + m_segments.push(myHead); + other.m_segments.push(otherHead); validatePrevious(); other.validatePrevious(); @@ -198,21 +146,21 @@ void MarkStackArray::stealSomeCellsFrom(MarkStackArray& other, size_t idleThread other.validatePrevious(); // If other has an entire segment, steal it and return. - if (other.m_topSegment->m_previous) { - ASSERT(other.m_topSegment->m_previous->m_top == m_segmentCapacity); - - // First remove a segment from other. - MarkStackSegment* current = other.m_topSegment->m_previous; - other.m_topSegment->m_previous = current->m_previous; - other.m_numberOfPreviousSegments--; - - ASSERT(!!other.m_numberOfPreviousSegments == !!other.m_topSegment->m_previous); - - // Now add it to this. - current->m_previous = m_topSegment->m_previous; - m_topSegment->m_previous = current; - m_numberOfPreviousSegments++; - + if (other.m_numberOfSegments > 1) { + // Move the heads of the lists aside. We'll push them back on after. + MarkStackSegment* otherHead = other.m_segments.removeHead(); + MarkStackSegment* myHead = m_segments.removeHead(); + + ASSERT(other.m_segments.head()->m_top == m_segmentCapacity); + + m_segments.push(other.m_segments.removeHead()); + + m_numberOfSegments++; + other.m_numberOfSegments--; + + m_segments.push(myHead); + other.m_segments.push(otherHead); + validatePrevious(); other.validatePrevious(); return; diff --git a/Source/JavaScriptCore/heap/MarkStack.h b/Source/JavaScriptCore/heap/MarkStack.h index 0245e4be5..2a7f04450 100644 --- a/Source/JavaScriptCore/heap/MarkStack.h +++ b/Source/JavaScriptCore/heap/MarkStack.h @@ -27,19 +27,19 @@ #define MarkStack_h #if ENABLE(OBJECT_MARK_LOGGING) -#define MARK_LOG_MESSAGE0(message) dataLog(message) -#define MARK_LOG_MESSAGE1(message, arg1) dataLog(message, arg1) -#define MARK_LOG_MESSAGE2(message, arg1, arg2) dataLog(message, arg1, arg2) +#define MARK_LOG_MESSAGE0(message) dataLogF(message) +#define MARK_LOG_MESSAGE1(message, arg1) dataLogF(message, arg1) +#define MARK_LOG_MESSAGE2(message, arg1, arg2) dataLogF(message, arg1, arg2) #define MARK_LOG_ROOT(visitor, rootName) \ - dataLog("\n%s: ", rootName); \ + dataLogF("\n%s: ", rootName); \ (visitor).resetChildCount() #define MARK_LOG_PARENT(visitor, parent) \ - dataLog("\n%p (%s): ", parent, parent->className() ? parent->className() : "unknown"); \ + dataLogF("\n%p (%s): ", parent, parent->className() ? parent->className() : "unknown"); \ (visitor).resetChildCount() #define MARK_LOG_CHILD(visitor, child) \ if ((visitor).childCount()) \ - dataLogString(", "); \ - dataLog("%p", child); \ + dataLogFString(", "); \ + dataLogF("%p", child); \ (visitor).incrementChildCount() #else #define MARK_LOG_MESSAGE0(message) do { } while (false) @@ -50,19 +50,27 @@ #define MARK_LOG_CHILD(visitor, child) do { } while (false) #endif +#include "HeapBlock.h" #include <wtf/StdLibExtras.h> -#include <wtf/TCSpinLock.h> namespace JSC { +class BlockAllocator; +class DeadBlock; class JSCell; -struct MarkStackSegment { - MarkStackSegment* m_previous; +class MarkStackSegment : public HeapBlock<MarkStackSegment> { +public: + MarkStackSegment(Region* region) + : HeapBlock<MarkStackSegment>(region) #if !ASSERT_DISABLED - size_t m_top; + , m_top(0) #endif - + { + } + + static MarkStackSegment* create(DeadBlock*); + const JSCell** data() { return bitwise_cast<const JSCell**>(this + 1); @@ -77,26 +85,17 @@ struct MarkStackSegment { { return sizeof(MarkStackSegment) + capacity * sizeof(const JSCell*); } -}; -class MarkStackSegmentAllocator { -public: - MarkStackSegmentAllocator(); - ~MarkStackSegmentAllocator(); - - MarkStackSegment* allocate(); - void release(MarkStackSegment*); - - void shrinkReserve(); - -private: - SpinLock m_lock; - MarkStackSegment* m_nextFreeSegment; + static const size_t blockSize = 4 * KB; + +#if !ASSERT_DISABLED + size_t m_top; +#endif }; class MarkStackArray { public: - MarkStackArray(MarkStackSegmentAllocator&); + MarkStackArray(BlockAllocator&); ~MarkStackArray(); void append(const JSCell*); @@ -122,12 +121,12 @@ private: void validatePrevious(); - MarkStackSegment* m_topSegment; - MarkStackSegmentAllocator& m_allocator; + DoublyLinkedList<MarkStackSegment> m_segments; + BlockAllocator& m_blockAllocator; size_t m_segmentCapacity; size_t m_top; - size_t m_numberOfPreviousSegments; + size_t m_numberOfSegments; }; diff --git a/Source/JavaScriptCore/heap/MarkStackInlineMethods.h b/Source/JavaScriptCore/heap/MarkStackInlines.h index d3276d7fa..1595e843e 100644 --- a/Source/JavaScriptCore/heap/MarkStackInlineMethods.h +++ b/Source/JavaScriptCore/heap/MarkStackInlines.h @@ -23,43 +23,48 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MarkStackInlineMethods_h -#define MarkStackInlineMethods_h +#ifndef MarkStackInlines_h +#define MarkStackInlines_h #include "GCThreadSharedData.h" #include "MarkStack.h" namespace JSC { +inline MarkStackSegment* MarkStackSegment::create(DeadBlock* block) +{ + return new (NotNull, block) MarkStackSegment(block->region()); +} + inline size_t MarkStackArray::postIncTop() { size_t result = m_top++; - ASSERT(result == m_topSegment->m_top++); + ASSERT(result == m_segments.head()->m_top++); return result; } - + inline size_t MarkStackArray::preDecTop() { size_t result = --m_top; - ASSERT(result == --m_topSegment->m_top); + ASSERT(result == --m_segments.head()->m_top); return result; } - + inline void MarkStackArray::setTopForFullSegment() { - ASSERT(m_topSegment->m_top == m_segmentCapacity); + ASSERT(m_segments.head()->m_top == m_segmentCapacity); m_top = m_segmentCapacity; } inline void MarkStackArray::setTopForEmptySegment() { - ASSERT(!m_topSegment->m_top); + ASSERT(!m_segments.head()->m_top); m_top = 0; } inline size_t MarkStackArray::top() { - ASSERT(m_top == m_topSegment->m_top); + ASSERT(m_top == m_segments.head()->m_top); return m_top; } @@ -69,9 +74,9 @@ inline void MarkStackArray::validatePrevious() { } inline void MarkStackArray::validatePrevious() { unsigned count = 0; - for (MarkStackSegment* current = m_topSegment->m_previous; current; current = current->m_previous) + for (MarkStackSegment* current = m_segments.head(); current; current = current->next()) count++; - ASSERT(count == m_numberOfPreviousSegments); + ASSERT(m_segments.size() == m_numberOfSegments); } #endif @@ -79,7 +84,7 @@ inline void MarkStackArray::append(const JSCell* cell) { if (m_top == m_segmentCapacity) expand(); - m_topSegment->data()[postIncTop()] = cell; + m_segments.head()->data()[postIncTop()] = cell; } inline bool MarkStackArray::canRemoveLast() @@ -89,15 +94,15 @@ inline bool MarkStackArray::canRemoveLast() inline const JSCell* MarkStackArray::removeLast() { - return m_topSegment->data()[preDecTop()]; + return m_segments.head()->data()[preDecTop()]; } inline bool MarkStackArray::isEmpty() { if (m_top) return false; - if (m_topSegment->m_previous) { - ASSERT(m_topSegment->m_previous->m_top == m_segmentCapacity); + if (m_segments.head()->next()) { + ASSERT(m_segments.head()->next()->m_top == m_segmentCapacity); return false; } return true; @@ -105,9 +110,10 @@ inline bool MarkStackArray::isEmpty() inline size_t MarkStackArray::size() { - return m_top + m_segmentCapacity * m_numberOfPreviousSegments; + return m_top + m_segmentCapacity * (m_numberOfSegments - 1); } } // namespace JSC -#endif +#endif // MarkStackInlines_h + diff --git a/Source/JavaScriptCore/heap/MarkedBlock.h b/Source/JavaScriptCore/heap/MarkedBlock.h index f2f2a720d..9080aaef4 100644 --- a/Source/JavaScriptCore/heap/MarkedBlock.h +++ b/Source/JavaScriptCore/heap/MarkedBlock.h @@ -40,7 +40,7 @@ #if HEAP_LOG_BLOCK_STATE_TRANSITIONS #define HEAP_LOG_BLOCK_STATE_TRANSITION(block) do { \ - dataLog( \ + dataLogF( \ "%s:%d %s: block %s = %p, %d\n", \ __FILE__, __LINE__, __FUNCTION__, \ #block, (block), (block)->m_state); \ diff --git a/Source/JavaScriptCore/heap/SlotVisitor.cpp b/Source/JavaScriptCore/heap/SlotVisitor.cpp index 3919705d0..3ff4b48fa 100644 --- a/Source/JavaScriptCore/heap/SlotVisitor.cpp +++ b/Source/JavaScriptCore/heap/SlotVisitor.cpp @@ -1,9 +1,10 @@ #include "config.h" #include "SlotVisitor.h" +#include "SlotVisitorInlines.h" #include "ConservativeRoots.h" #include "CopiedSpace.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "GCThread.h" #include "JSArray.h" #include "JSDestructibleObject.h" @@ -15,7 +16,7 @@ namespace JSC { SlotVisitor::SlotVisitor(GCThreadSharedData& shared) - : m_stack(shared.m_segmentAllocator) + : m_stack(shared.m_globalData->heap.blockAllocator()) , m_visitCount(0) , m_isInParallelMode(false) , m_shared(shared) @@ -335,12 +336,12 @@ void SlotVisitor::finalizeUnconditionalFinalizers() void SlotVisitor::validate(JSCell* cell) { if (!cell) { - dataLog("cell is NULL\n"); + dataLogF("cell is NULL\n"); CRASH(); } if (!cell->structure()) { - dataLog("cell at %p has a null structure\n" , cell); + dataLogF("cell at %p has a null structure\n" , cell); CRASH(); } @@ -353,7 +354,7 @@ void SlotVisitor::validate(JSCell* cell) parentClassName = cell->structure()->structure()->JSCell::classInfo()->className; if (cell->structure()->JSCell::classInfo()) ourClassName = cell->structure()->JSCell::classInfo()->className; - dataLog("parent structure (%p <%s>) of cell at %p doesn't match cell's structure (%p <%s>)\n", + dataLogF("parent structure (%p <%s>) of cell at %p doesn't match cell's structure (%p <%s>)\n", cell->structure()->structure(), parentClassName, cell, cell->structure(), ourClassName); CRASH(); } diff --git a/Source/JavaScriptCore/heap/SlotVisitor.h b/Source/JavaScriptCore/heap/SlotVisitor.h index dcd4b75ef..53c7de64f 100644 --- a/Source/JavaScriptCore/heap/SlotVisitor.h +++ b/Source/JavaScriptCore/heap/SlotVisitor.h @@ -27,7 +27,7 @@ #define SlotVisitor_h #include "HandleTypes.h" -#include "MarkStackInlineMethods.h" +#include "MarkStackInlines.h" #include <wtf/text/StringHash.h> @@ -36,6 +36,7 @@ namespace JSC { class ConservativeRoots; class GCThreadSharedData; class Heap; +template<typename T> class Weak; template<typename T> class WriteBarrierBase; template<typename T> class JITWriteBarrier; @@ -56,6 +57,8 @@ public: template<typename T> void appendUnbarrieredPointer(T**); void appendUnbarrieredValue(JSValue*); + template<typename T> + void appendUnbarrieredWeak(Weak<T>*); void addOpaqueRoot(void*); bool containsOpaqueRoot(void*); diff --git a/Source/JavaScriptCore/heap/SlotVisitorInlineMethods.h b/Source/JavaScriptCore/heap/SlotVisitorInlines.h index e5908bf36..ea8126f87 100644 --- a/Source/JavaScriptCore/heap/SlotVisitorInlineMethods.h +++ b/Source/JavaScriptCore/heap/SlotVisitorInlines.h @@ -23,12 +23,13 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SlotVisitorInlineMethods_h -#define SlotVisitorInlineMethods_h +#ifndef SlotVisitorInlines_h +#define SlotVisitorInlines_h -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "Options.h" #include "SlotVisitor.h" +#include "Weak.h" namespace JSC { @@ -66,6 +67,14 @@ ALWAYS_INLINE void SlotVisitor::append(JSCell** slot) internalAppend(*slot); } +template<typename T> +ALWAYS_INLINE void SlotVisitor::appendUnbarrieredWeak(Weak<T>* weak) +{ + ASSERT(weak); + if (weak->get()) + internalAppend(weak->get()); +} + ALWAYS_INLINE void SlotVisitor::internalAppend(JSValue value) { if (!value || !value.isCell()) @@ -170,5 +179,5 @@ inline void SlotVisitor::copyLater(void* ptr, size_t bytes) } // namespace JSC -#endif // SlotVisitorInlineMethods_h +#endif // SlotVisitorInlines_h diff --git a/Source/JavaScriptCore/heap/Weak.h b/Source/JavaScriptCore/heap/Weak.h index 3c3d1d0ce..efb2a9a56 100644 --- a/Source/JavaScriptCore/heap/Weak.h +++ b/Source/JavaScriptCore/heap/Weak.h @@ -151,6 +151,11 @@ template<typename T> inline WeakImpl* Weak<T>::hashTableDeletedValue() return reinterpret_cast<WeakImpl*>(-1); } +template <typename T> inline bool operator==(const Weak<T>& lhs, const Weak<T>& rhs) +{ + return lhs.get() == rhs.get(); +} + // This function helps avoid modifying a weak table while holding an iterator into it. (Object allocation // can run a finalizer that modifies the table. We avoid that by requiring a pre-constructed object as our value.) template<typename Map, typename Key, typename Value> inline void weakAdd(Map& map, const Key& key, Value value) diff --git a/Source/JavaScriptCore/interpreter/CallFrame.cpp b/Source/JavaScriptCore/interpreter/CallFrame.cpp index 6dcf354b3..ac286c36c 100644 --- a/Source/JavaScriptCore/interpreter/CallFrame.cpp +++ b/Source/JavaScriptCore/interpreter/CallFrame.cpp @@ -40,7 +40,7 @@ void CallFrame::dumpCaller() JSValue function; interpreter()->retrieveLastCaller(this, signedLineNumber, sourceID, urlString, function); - dataLog("Callpoint => %s:%d\n", urlString.utf8().data(), signedLineNumber); + dataLogF("Callpoint => %s:%d\n", urlString.utf8().data(), signedLineNumber); } JSStack* CallFrame::stack() diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp index 397ac8474..ad89505fc 100644 --- a/Source/JavaScriptCore/interpreter/Interpreter.cpp +++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp @@ -380,16 +380,16 @@ void Interpreter::dumpCallFrame(CallFrame*) void Interpreter::dumpCallFrame(CallFrame* callFrame) { - callFrame->codeBlock()->dump(callFrame); + callFrame->codeBlock()->dump(); dumpRegisters(callFrame); } void Interpreter::dumpRegisters(CallFrame* callFrame) { - dataLog("Register frame: \n\n"); - dataLog("-----------------------------------------------------------------------------\n"); - dataLog(" use | address | value \n"); - dataLog("-----------------------------------------------------------------------------\n"); + dataLogF("Register frame: \n\n"); + dataLogF("-----------------------------------------------------------------------------\n"); + dataLogF(" use | address | value \n"); + dataLogF("-----------------------------------------------------------------------------\n"); CodeBlock* codeBlock = callFrame->codeBlock(); const Register* it; @@ -401,32 +401,32 @@ void Interpreter::dumpRegisters(CallFrame* callFrame) JSValue v = it->jsValue(); int registerNumber = it - callFrame->registers(); String name = codeBlock->nameForRegister(registerNumber); - dataLog("[r% 3d %14s] | %10p | %-16s 0x%lld \n", registerNumber, name.ascii().data(), it, v.description(), (long long)JSValue::encode(v)); + dataLogF("[r% 3d %14s] | %10p | %-16s 0x%lld \n", registerNumber, name.ascii().data(), it, v.description(), (long long)JSValue::encode(v)); it++; } - dataLog("-----------------------------------------------------------------------------\n"); - dataLog("[ArgumentCount] | %10p | %lu \n", it, (unsigned long) callFrame->argumentCount()); + dataLogF("-----------------------------------------------------------------------------\n"); + dataLogF("[ArgumentCount] | %10p | %lu \n", it, (unsigned long) callFrame->argumentCount()); ++it; - dataLog("[CallerFrame] | %10p | %p \n", it, callFrame->callerFrame()); + dataLogF("[CallerFrame] | %10p | %p \n", it, callFrame->callerFrame()); ++it; - dataLog("[Callee] | %10p | %p \n", it, callFrame->callee()); + dataLogF("[Callee] | %10p | %p \n", it, callFrame->callee()); ++it; - dataLog("[ScopeChain] | %10p | %p \n", it, callFrame->scope()); + dataLogF("[ScopeChain] | %10p | %p \n", it, callFrame->scope()); ++it; #if ENABLE(JIT) AbstractPC pc = callFrame->abstractReturnPC(callFrame->globalData()); if (pc.hasJITReturnAddress()) - dataLog("[ReturnJITPC] | %10p | %p \n", it, pc.jitReturnAddress().value()); + dataLogF("[ReturnJITPC] | %10p | %p \n", it, pc.jitReturnAddress().value()); #endif unsigned bytecodeOffset = 0; int line = 0; getCallerInfo(&callFrame->globalData(), callFrame, line, bytecodeOffset); - dataLog("[ReturnVPC] | %10p | %d (line %d)\n", it, bytecodeOffset, line); + dataLogF("[ReturnVPC] | %10p | %d (line %d)\n", it, bytecodeOffset, line); ++it; - dataLog("[CodeBlock] | %10p | %p \n", it, callFrame->codeBlock()); + dataLogF("[CodeBlock] | %10p | %p \n", it, callFrame->codeBlock()); ++it; - dataLog("-----------------------------------------------------------------------------\n"); + dataLogF("-----------------------------------------------------------------------------\n"); int registerCount = 0; @@ -436,23 +436,23 @@ void Interpreter::dumpRegisters(CallFrame* callFrame) JSValue v = it->jsValue(); int registerNumber = it - callFrame->registers(); String name = codeBlock->nameForRegister(registerNumber); - dataLog("[r% 3d %14s] | %10p | %-16s 0x%lld \n", registerNumber, name.ascii().data(), it, v.description(), (long long)JSValue::encode(v)); + dataLogF("[r% 3d %14s] | %10p | %-16s 0x%lld \n", registerNumber, name.ascii().data(), it, v.description(), (long long)JSValue::encode(v)); ++it; ++registerCount; } while (it != end); } - dataLog("-----------------------------------------------------------------------------\n"); + dataLogF("-----------------------------------------------------------------------------\n"); end = it + codeBlock->m_numCalleeRegisters - codeBlock->m_numVars; if (it != end) { do { JSValue v = (*it).jsValue(); - dataLog("[r% 3d] | %10p | %-16s 0x%lld \n", registerCount, it, v.description(), (long long)JSValue::encode(v)); + dataLogF("[r% 3d] | %10p | %-16s 0x%lld \n", registerCount, it, v.description(), (long long)JSValue::encode(v)); ++it; ++registerCount; } while (it != end); } - dataLog("-----------------------------------------------------------------------------\n"); + dataLogF("-----------------------------------------------------------------------------\n"); } #endif diff --git a/Source/JavaScriptCore/jit/ClosureCallStubRoutine.cpp b/Source/JavaScriptCore/jit/ClosureCallStubRoutine.cpp new file mode 100644 index 000000000..73704aa03 --- /dev/null +++ b/Source/JavaScriptCore/jit/ClosureCallStubRoutine.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ClosureCallStubRoutine.h" + +#if ENABLE(JIT) + +#include "Executable.h" +#include "Heap.h" +#include "JSGlobalData.h" +#include "SlotVisitor.h" +#include "Structure.h" + +namespace JSC { + +ClosureCallStubRoutine::ClosureCallStubRoutine( + const MacroAssemblerCodeRef& code, JSGlobalData& globalData, const JSCell* owner, + Structure* structure, ExecutableBase* executable, const CodeOrigin& codeOrigin) + : GCAwareJITStubRoutine(code, globalData, true) + , m_structure(globalData, owner, structure) + , m_executable(globalData, owner, executable) + , m_codeOrigin(codeOrigin) +{ +} + +ClosureCallStubRoutine::~ClosureCallStubRoutine() +{ +} + +void ClosureCallStubRoutine::markRequiredObjectsInternal(SlotVisitor& visitor) +{ + visitor.append(&m_structure); + visitor.append(&m_executable); +} + +} // namespace JSC + +#endif // ENABLE(JIT) + diff --git a/Source/JavaScriptCore/jit/ClosureCallStubRoutine.h b/Source/JavaScriptCore/jit/ClosureCallStubRoutine.h new file mode 100644 index 000000000..3fd020691 --- /dev/null +++ b/Source/JavaScriptCore/jit/ClosureCallStubRoutine.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ClosureCallStubRoutine_h +#define ClosureCallStubRoutine_h + +#include <wtf/Platform.h> + +#if ENABLE(JIT) + +#include "CodeOrigin.h" +#include "GCAwareJITStubRoutine.h" + +namespace JSC { + +class ClosureCallStubRoutine : public GCAwareJITStubRoutine { +public: + ClosureCallStubRoutine( + const MacroAssemblerCodeRef&, JSGlobalData&, const JSCell* owner, + Structure*, ExecutableBase*, const CodeOrigin&); + + virtual ~ClosureCallStubRoutine(); + + Structure* structure() const { return m_structure.get(); } + ExecutableBase* executable() const { return m_executable.get(); } + const CodeOrigin& codeOrigin() const { return m_codeOrigin; } + +protected: + virtual void markRequiredObjectsInternal(SlotVisitor&); + +private: + WriteBarrier<Structure> m_structure; + WriteBarrier<ExecutableBase> m_executable; + // This allows us to figure out who a call is linked to by searching through + // stub routines. + CodeOrigin m_codeOrigin; +}; + +} // namespace JSC + +#endif // ENABLE(JIT) + +#endif // ClosureCallStubRoutine_h + diff --git a/Source/JavaScriptCore/jit/GCAwareJITStubRoutine.cpp b/Source/JavaScriptCore/jit/GCAwareJITStubRoutine.cpp index 0f0eb14b7..521e49751 100644 --- a/Source/JavaScriptCore/jit/GCAwareJITStubRoutine.cpp +++ b/Source/JavaScriptCore/jit/GCAwareJITStubRoutine.cpp @@ -30,17 +30,17 @@ #include "Heap.h" #include "JSGlobalData.h" - #include "SlotVisitor.h" #include "Structure.h" namespace JSC { GCAwareJITStubRoutine::GCAwareJITStubRoutine( - const MacroAssemblerCodeRef& code, JSGlobalData& globalData) + const MacroAssemblerCodeRef& code, JSGlobalData& globalData, bool isClosureCall) : JITStubRoutine(code) , m_mayBeExecuting(false) , m_isJettisoned(false) + , m_isClosureCall(isClosureCall) { globalData.heap.m_jitStubRoutines.add(this); } diff --git a/Source/JavaScriptCore/jit/GCAwareJITStubRoutine.h b/Source/JavaScriptCore/jit/GCAwareJITStubRoutine.h index 59bc76beb..e5ce281e8 100644 --- a/Source/JavaScriptCore/jit/GCAwareJITStubRoutine.h +++ b/Source/JavaScriptCore/jit/GCAwareJITStubRoutine.h @@ -54,7 +54,7 @@ class JITStubRoutineSet; // list which does not get reclaimed all at once). class GCAwareJITStubRoutine : public JITStubRoutine { public: - GCAwareJITStubRoutine(const MacroAssemblerCodeRef&, JSGlobalData&); + GCAwareJITStubRoutine(const MacroAssemblerCodeRef&, JSGlobalData&, bool isClosureCall = false); virtual ~GCAwareJITStubRoutine(); void markRequiredObjects(SlotVisitor& visitor) @@ -64,6 +64,8 @@ public: void deleteFromGC(); + bool isClosureCall() const { return m_isClosureCall; } + protected: virtual void observeZeroRefCount(); @@ -74,6 +76,7 @@ private: bool m_mayBeExecuting; bool m_isJettisoned; + bool m_isClosureCall; }; // Use this if you want to mark one additional object during GC if your stub diff --git a/Source/JavaScriptCore/jit/HostCallReturnValue.cpp b/Source/JavaScriptCore/jit/HostCallReturnValue.cpp index c4d2e6ad9..967c499b9 100644 --- a/Source/JavaScriptCore/jit/HostCallReturnValue.cpp +++ b/Source/JavaScriptCore/jit/HostCallReturnValue.cpp @@ -29,7 +29,7 @@ #include "CallFrame.h" #include <wtf/InlineASM.h> #include "JSObject.h" -#include "JSValueInlineMethods.h" +#include "JSValueInlines.h" namespace JSC { diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp index 3102c7693..cccf33bf6 100644 --- a/Source/JavaScriptCore/jit/JIT.cpp +++ b/Source/JavaScriptCore/jit/JIT.cpp @@ -38,7 +38,7 @@ JSC::MacroAssemblerX86Common::SSE2CheckState JSC::MacroAssemblerX86Common::s_sse #include <wtf/CryptographicallyRandomNumber.h> #include "DFGNode.h" // for DFG_SUCCESS_STATS #include "Interpreter.h" -#include "JITInlineMethods.h" +#include "JITInlines.h" #include "JITStubCall.h" #include "JSArray.h" #include "JSFunction.h" @@ -212,6 +212,8 @@ void JIT::privateCompileMainPass() m_callLinkInfoIndex = 0; for (m_bytecodeOffset = 0; m_bytecodeOffset < instructionCount; ) { + if (m_disassembler) + m_disassembler->setForBytecodeMainPath(m_bytecodeOffset, label()); Instruction* currentInstruction = instructionsBegin + m_bytecodeOffset; ASSERT_WITH_MESSAGE(m_interpreter->isOpcode(currentInstruction->u.opcode), "privateCompileMainPass gone bad @ %d", m_bytecodeOffset); @@ -228,7 +230,7 @@ void JIT::privateCompileMainPass() m_labels[m_bytecodeOffset] = label(); #if ENABLE(JIT_VERBOSE) - dataLog("Old JIT emitting code for bc#%u at offset 0x%lx.\n", m_bytecodeOffset, (long)debugOffset()); + dataLogF("Old JIT emitting code for bc#%u at offset 0x%lx.\n", m_bytecodeOffset, (long)debugOffset()); #endif switch (m_interpreter->getOpcodeID(currentInstruction->u.opcode)) { @@ -251,6 +253,7 @@ void JIT::privateCompileMainPass() DEFINE_OP(op_call_varargs) DEFINE_OP(op_catch) DEFINE_OP(op_construct) + DEFINE_OP(op_get_callee) DEFINE_OP(op_create_this) DEFINE_OP(op_convert_this) DEFINE_OP(op_init_lazy_reg) @@ -448,8 +451,11 @@ void JIT::privateCompileSlowCases() #endif #if ENABLE(JIT_VERBOSE) - dataLog("Old JIT emitting slow code for bc#%u at offset 0x%lx.\n", m_bytecodeOffset, (long)debugOffset()); + dataLogF("Old JIT emitting slow code for bc#%u at offset 0x%lx.\n", m_bytecodeOffset, (long)debugOffset()); #endif + + if (m_disassembler) + m_disassembler->setForBytecodeSlowPath(m_bytecodeOffset, label()); switch (m_interpreter->getOpcodeID(currentInstruction->u.opcode)) { DEFINE_SLOWCASE_OP(op_add) @@ -624,6 +630,12 @@ JITCode JIT::privateCompile(CodePtr* functionEntryArityCheck, JITCompilationEffo break; } #endif + + if (Options::showDisassembly()) + m_disassembler = adoptPtr(new JITDisassembler(m_codeBlock)); + + if (m_disassembler) + m_disassembler->setStartOfCode(label()); // Just add a little bit of randomness to the codegen if (m_randomGenerator.getUint32() & 1) @@ -683,6 +695,9 @@ JITCode JIT::privateCompile(CodePtr* functionEntryArityCheck, JITCompilationEffo privateCompileMainPass(); privateCompileLinkPass(); privateCompileSlowCases(); + + if (m_disassembler) + m_disassembler->setEndOfSlowPath(label()); Label arityCheck; if (m_codeBlock->codeType() == FunctionCode) { @@ -712,6 +727,9 @@ JITCode JIT::privateCompile(CodePtr* functionEntryArityCheck, JITCompilationEffo } ASSERT(m_jmpTable.isEmpty()); + + if (m_disassembler) + m_disassembler->setEndOfCode(label()); LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock, effort); if (patchBuffer.didFailToAllocate()) @@ -780,10 +798,11 @@ JITCode JIT::privateCompile(CodePtr* functionEntryArityCheck, JITCompilationEffo for (unsigned i = 0; i < m_codeBlock->numberOfCallLinkInfos(); ++i) { CallLinkInfo& info = m_codeBlock->callLinkInfo(i); info.callType = m_callStructureStubCompilationInfo[i].callType; - info.bytecodeIndex = m_callStructureStubCompilationInfo[i].bytecodeIndex; + info.codeOrigin = CodeOrigin(m_callStructureStubCompilationInfo[i].bytecodeIndex); info.callReturnLocation = patchBuffer.locationOfNearCall(m_callStructureStubCompilationInfo[i].callReturnLocation); info.hotPathBegin = patchBuffer.locationOf(m_callStructureStubCompilationInfo[i].hotPathBegin); info.hotPathOther = patchBuffer.locationOfNearCall(m_callStructureStubCompilationInfo[i].hotPathOther); + info.calleeGPR = regT0; } #if ENABLE(DFG_JIT) || ENABLE(LLINT) @@ -803,11 +822,11 @@ JITCode JIT::privateCompile(CodePtr* functionEntryArityCheck, JITCompilationEffo if (m_codeBlock->codeType() == FunctionCode && functionEntryArityCheck) *functionEntryArityCheck = patchBuffer.locationOf(arityCheck); + + if (m_disassembler) + m_disassembler->dump(patchBuffer); - CodeRef result = FINALIZE_CODE( - patchBuffer, - ("Baseline JIT code for CodeBlock %p, instruction count = %u", - m_codeBlock, m_codeBlock->instructionCount())); + CodeRef result = patchBuffer.finalizeCodeWithoutDisassembly(); m_globalData->machineCodeBytesPerBytecodeWordForBaselineJIT.add( static_cast<double>(result.size()) / @@ -816,7 +835,7 @@ JITCode JIT::privateCompile(CodePtr* functionEntryArityCheck, JITCompilationEffo m_codeBlock->shrinkToFit(CodeBlock::LateShrink); #if ENABLE(JIT_VERBOSE) - dataLog("JIT generated code for %p at [%p, %p).\n", m_codeBlock, result.executableMemory()->start(), result.executableMemory()->end()); + dataLogF("JIT generated code for %p at [%p, %p).\n", m_codeBlock, result.executableMemory()->start(), result.executableMemory()->end()); #endif return JITCode(result, JITCode::BaselineJIT); diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h index dcf87d352..c0d60add1 100644 --- a/Source/JavaScriptCore/jit/JIT.h +++ b/Source/JavaScriptCore/jit/JIT.h @@ -46,6 +46,7 @@ #include "CodeBlock.h" #include "CompactJITCodeMap.h" #include "Interpreter.h" +#include "JITDisassembler.h" #include "JSInterfaceJIT.h" #include "Opcode.h" #include "Profiler.h" @@ -474,7 +475,9 @@ namespace JSC { // Property is int-checked and zero extended. Base is cell checked. // Structure is already profiled. Returns the slow cases. Fall-through // case contains result in regT0, and it is not yet profiled. - JumpList emitContiguousGetByVal(Instruction*, PatchableJump& badType); + JumpList emitInt32GetByVal(Instruction* instruction, PatchableJump& badType) { return emitContiguousGetByVal(instruction, badType, Int32Shape); } + JumpList emitDoubleGetByVal(Instruction*, PatchableJump& badType); + JumpList emitContiguousGetByVal(Instruction*, PatchableJump& badType, IndexingType expectedShape = ContiguousShape); JumpList emitArrayStorageGetByVal(Instruction*, PatchableJump& badType); JumpList emitIntTypedArrayGetByVal(Instruction*, PatchableJump& badType, const TypedArrayDescriptor&, size_t elementSize, TypedArraySignedness); JumpList emitFloatTypedArrayGetByVal(Instruction*, PatchableJump& badType, const TypedArrayDescriptor&, size_t elementSize); @@ -483,7 +486,19 @@ namespace JSC { // The value to store is not yet loaded. Property is int-checked and // zero-extended. Base is cell checked. Structure is already profiled. // returns the slow cases. - JumpList emitContiguousPutByVal(Instruction*, PatchableJump& badType); + JumpList emitInt32PutByVal(Instruction* currentInstruction, PatchableJump& badType) + { + return emitGenericContiguousPutByVal(currentInstruction, badType, Int32Shape); + } + JumpList emitDoublePutByVal(Instruction* currentInstruction, PatchableJump& badType) + { + return emitGenericContiguousPutByVal(currentInstruction, badType, DoubleShape); + } + JumpList emitContiguousPutByVal(Instruction* currentInstruction, PatchableJump& badType) + { + return emitGenericContiguousPutByVal(currentInstruction, badType); + } + JumpList emitGenericContiguousPutByVal(Instruction*, PatchableJump& badType, IndexingType indexingShape = ContiguousShape); JumpList emitArrayStoragePutByVal(Instruction*, PatchableJump& badType); JumpList emitIntTypedArrayPutByVal(Instruction*, PatchableJump& badType, const TypedArrayDescriptor&, size_t elementSize, TypedArraySignedness, TypedArrayRounding); JumpList emitFloatTypedArrayPutByVal(Instruction*, PatchableJump& badType, const TypedArrayDescriptor&, size_t elementSize); @@ -632,6 +647,7 @@ namespace JSC { void emit_op_call_put_result(Instruction*); void emit_op_catch(Instruction*); void emit_op_construct(Instruction*); + void emit_op_get_callee(Instruction*); void emit_op_create_this(Instruction*); void emit_op_convert_this(Instruction*); void emit_op_create_arguments(Instruction*); @@ -930,6 +946,7 @@ namespace JSC { int m_uninterruptedConstantSequenceBegin; #endif #endif + OwnPtr<JITDisassembler> m_disassembler; WeakRandom m_randomGenerator; static CodeRef stringGetByValStubGenerator(JSGlobalData*); diff --git a/Source/JavaScriptCore/jit/JITArithmetic.cpp b/Source/JavaScriptCore/jit/JITArithmetic.cpp index 21d59bc33..bcb3dd74a 100644 --- a/Source/JavaScriptCore/jit/JITArithmetic.cpp +++ b/Source/JavaScriptCore/jit/JITArithmetic.cpp @@ -29,7 +29,7 @@ #include "JIT.h" #include "CodeBlock.h" -#include "JITInlineMethods.h" +#include "JITInlines.h" #include "JITStubCall.h" #include "JITStubs.h" #include "JSArray.h" @@ -1090,18 +1090,20 @@ void JIT::emit_op_div(Instruction* currentInstruction) // access). So if we are DFG compiling anything in the program, we want this code to // ensure that it produces integers whenever possible. - // FIXME: This will fail to convert to integer if the result is zero. We should - // distinguish between positive zero and negative zero here. - JumpList notInteger; branchConvertDoubleToInt32(fpRegT0, regT0, notInteger, fpRegT1); // If we've got an integer, we might as well make that the result of the division. emitFastArithReTagImmediate(regT0, regT0); Jump isInteger = jump(); notInteger.link(this); - add32(TrustedImm32(1), AbsoluteAddress(&m_codeBlock->addSpecialFastCaseProfile(m_bytecodeOffset)->m_counter)); moveDoubleTo64(fpRegT0, regT0); + Jump doubleZero = branchTest64(Zero, regT0); + add32(TrustedImm32(1), AbsoluteAddress(&m_codeBlock->addSpecialFastCaseProfile(m_bytecodeOffset)->m_counter)); sub64(tagTypeNumberRegister, regT0); + Jump trueDouble = jump(); + doubleZero.link(this); + move(tagTypeNumberRegister, regT0); + trueDouble.link(this); isInteger.link(this); #else // Double result. diff --git a/Source/JavaScriptCore/jit/JITArithmetic32_64.cpp b/Source/JavaScriptCore/jit/JITArithmetic32_64.cpp index 62a359eeb..960d06091 100644 --- a/Source/JavaScriptCore/jit/JITArithmetic32_64.cpp +++ b/Source/JavaScriptCore/jit/JITArithmetic32_64.cpp @@ -30,7 +30,7 @@ #include "JIT.h" #include "CodeBlock.h" -#include "JITInlineMethods.h" +#include "JITInlines.h" #include "JITStubCall.h" #include "JITStubs.h" #include "JSArray.h" diff --git a/Source/JavaScriptCore/jit/JITCall.cpp b/Source/JavaScriptCore/jit/JITCall.cpp index 074bf7f97..006c5b741 100644 --- a/Source/JavaScriptCore/jit/JITCall.cpp +++ b/Source/JavaScriptCore/jit/JITCall.cpp @@ -31,7 +31,7 @@ #include "Arguments.h" #include "CodeBlock.h" -#include "JITInlineMethods.h" +#include "JITInlines.h" #include "JITStubCall.h" #include "JSArray.h" #include "JSFunction.h" diff --git a/Source/JavaScriptCore/jit/JITCall32_64.cpp b/Source/JavaScriptCore/jit/JITCall32_64.cpp index ad827cdf9..ecd5cf126 100644 --- a/Source/JavaScriptCore/jit/JITCall32_64.cpp +++ b/Source/JavaScriptCore/jit/JITCall32_64.cpp @@ -32,7 +32,7 @@ #include "Arguments.h" #include "CodeBlock.h" #include "Interpreter.h" -#include "JITInlineMethods.h" +#include "JITInlines.h" #include "JITStubCall.h" #include "JSArray.h" #include "JSFunction.h" diff --git a/Source/JavaScriptCore/jit/JITDisassembler.cpp b/Source/JavaScriptCore/jit/JITDisassembler.cpp new file mode 100644 index 000000000..35b939913 --- /dev/null +++ b/Source/JavaScriptCore/jit/JITDisassembler.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JITDisassembler.h" + +#if ENABLE(JIT) + +#include "CodeBlock.h" +#include "JIT.h" + +namespace JSC { + +JITDisassembler::JITDisassembler(CodeBlock *codeBlock) + : m_codeBlock(codeBlock) + , m_labelForBytecodeIndexInMainPath(codeBlock->instructionCount()) + , m_labelForBytecodeIndexInSlowPath(codeBlock->instructionCount()) +{ +} + +JITDisassembler::~JITDisassembler() +{ +} + +void JITDisassembler::dump(LinkBuffer& linkBuffer) +{ + dataLogF("Baseline JIT code for CodeBlock %p, instruction count = %u:\n", m_codeBlock, m_codeBlock->instructionCount()); + dataLogF(" Code at [%p, %p):\n", linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize()); + dumpDisassembly(linkBuffer, m_startOfCode, m_labelForBytecodeIndexInMainPath[0]); + + MacroAssembler::Label firstSlowLabel; + for (unsigned i = 0; i < m_labelForBytecodeIndexInSlowPath.size(); ++i) { + if (m_labelForBytecodeIndexInSlowPath[i].isSet()) { + firstSlowLabel = m_labelForBytecodeIndexInSlowPath[i]; + break; + } + } + dumpForInstructions(linkBuffer, " ", m_labelForBytecodeIndexInMainPath, firstSlowLabel.isSet() ? firstSlowLabel : m_endOfSlowPath); + dataLogF(" (End Of Main Path)\n"); + dumpForInstructions(linkBuffer, " (S) ", m_labelForBytecodeIndexInSlowPath, m_endOfSlowPath); + dataLogF(" (End Of Slow Path)\n"); + + dumpDisassembly(linkBuffer, m_endOfSlowPath, m_endOfCode); +} + +void JITDisassembler::dumpForInstructions(LinkBuffer& linkBuffer, const char* prefix, Vector<MacroAssembler::Label>& labels, MacroAssembler::Label endLabel) +{ + for (unsigned i = 0 ; i < labels.size();) { + if (!labels[i].isSet()) { + i++; + continue; + } + dataLogF("%s", prefix); + m_codeBlock->dump(i); + for (unsigned nextIndex = i + 1; ; nextIndex++) { + if (nextIndex >= labels.size()) { + dumpDisassembly(linkBuffer, labels[i], endLabel); + return; + } + if (labels[nextIndex].isSet()) { + dumpDisassembly(linkBuffer, labels[i], labels[nextIndex]); + i = nextIndex; + break; + } + } + } +} + +void JITDisassembler::dumpDisassembly(LinkBuffer& linkBuffer, MacroAssembler::Label from, MacroAssembler::Label to) +{ + CodeLocationLabel fromLocation = linkBuffer.locationOf(from); + CodeLocationLabel toLocation = linkBuffer.locationOf(to); + disassemble(fromLocation, bitwise_cast<uintptr_t>(toLocation.executableAddress()) - bitwise_cast<uintptr_t>(fromLocation.executableAddress()), " ", WTF::dataFile()); +} + +} // namespace JSC + +#endif // ENABLE(JIT) + diff --git a/Source/JavaScriptCore/jit/JITDisassembler.h b/Source/JavaScriptCore/jit/JITDisassembler.h new file mode 100644 index 000000000..f8e917d98 --- /dev/null +++ b/Source/JavaScriptCore/jit/JITDisassembler.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JITDisassembler_h +#define JITDisassembler_h + +#include <wtf/Platform.h> + +#if ENABLE(JIT) + +#include "LinkBuffer.h" +#include "MacroAssembler.h" +#include <wtf/Vector.h> + +namespace JSC { + +class CodeBlock; + +class JITDisassembler { + WTF_MAKE_FAST_ALLOCATED; +public: + JITDisassembler(CodeBlock*); + ~JITDisassembler(); + + void setStartOfCode(MacroAssembler::Label label) { m_startOfCode = label; } + void setForBytecodeMainPath(unsigned bytecodeIndex, MacroAssembler::Label label) + { + m_labelForBytecodeIndexInMainPath[bytecodeIndex] = label; + } + void setForBytecodeSlowPath(unsigned bytecodeIndex, MacroAssembler::Label label) + { + m_labelForBytecodeIndexInSlowPath[bytecodeIndex] = label; + } + void setEndOfSlowPath(MacroAssembler::Label label) { m_endOfSlowPath = label; } + void setEndOfCode(MacroAssembler::Label label) { m_endOfCode = label; } + + void dump(LinkBuffer&); + +private: + void dumpForInstructions(LinkBuffer&, const char* prefix, Vector<MacroAssembler::Label>& labels, MacroAssembler::Label endLabel); + void dumpDisassembly(LinkBuffer&, MacroAssembler::Label from, MacroAssembler::Label to); + + CodeBlock* m_codeBlock; + MacroAssembler::Label m_startOfCode; + Vector<MacroAssembler::Label> m_labelForBytecodeIndexInMainPath; + Vector<MacroAssembler::Label> m_labelForBytecodeIndexInSlowPath; + MacroAssembler::Label m_endOfSlowPath; + MacroAssembler::Label m_endOfCode; +}; + +} // namespace JSC + +#endif // ENABLE(JIT) + +#endif // JITDisassembler_h + diff --git a/Source/JavaScriptCore/jit/JITExceptions.cpp b/Source/JavaScriptCore/jit/JITExceptions.cpp index f6cec24bd..aeb869474 100644 --- a/Source/JavaScriptCore/jit/JITExceptions.cpp +++ b/Source/JavaScriptCore/jit/JITExceptions.cpp @@ -39,7 +39,7 @@ namespace JSC { ExceptionHandler genericThrow(JSGlobalData* globalData, ExecState* callFrame, JSValue exceptionValue, unsigned vPCIndex) { ASSERT(exceptionValue); - + globalData->exception = JSValue(); HandlerInfo* handler = globalData->interpreter->throwException(callFrame, exceptionValue, vPCIndex); // This may update callFrame & exceptionValue! globalData->exception = exceptionValue; diff --git a/Source/JavaScriptCore/jit/JITInlineMethods.h b/Source/JavaScriptCore/jit/JITInlines.h index 410bdf710..e6f95b94c 100644 --- a/Source/JavaScriptCore/jit/JITInlineMethods.h +++ b/Source/JavaScriptCore/jit/JITInlines.h @@ -23,8 +23,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef JITInlineMethods_h -#define JITInlineMethods_h +#ifndef JITInlines_h +#define JITInlines_h #if ENABLE(JIT) @@ -528,12 +528,12 @@ inline void JIT::emitArrayProfileStoreToHoleSpecialCase(ArrayProfile* arrayProfi #endif } -static inline bool arrayProfileSaw(ArrayProfile* profile, IndexingType capability) +static inline bool arrayProfileSaw(ArrayModes arrayModes, IndexingType capability) { #if ENABLE(VALUE_PROFILER) - return !!(profile->observedArrayModes() & (asArrayModes(NonArray | capability) | asArrayModes(ArrayClass | capability))); + return arrayModesInclude(arrayModes, capability); #else - UNUSED_PARAM(profile); + UNUSED_PARAM(arrayModes); UNUSED_PARAM(capability); return false; #endif @@ -541,9 +541,20 @@ static inline bool arrayProfileSaw(ArrayProfile* profile, IndexingType capabilit inline JITArrayMode JIT::chooseArrayMode(ArrayProfile* profile) { - if (arrayProfileSaw(profile, ArrayStorageShape)) +#if ENABLE(VALUE_PROFILER) + profile->computeUpdatedPrediction(m_codeBlock); + ArrayModes arrayModes = profile->observedArrayModes(); + if (arrayProfileSaw(arrayModes, DoubleShape)) + return JITDouble; + if (arrayProfileSaw(arrayModes, Int32Shape)) + return JITInt32; + if (arrayProfileSaw(arrayModes, ArrayStorageShape)) return JITArrayStorage; return JITContiguous; +#else + UNUSED_PARAM(profile); + return JITContiguous; +#endif } #if USE(JSVALUE32_64) @@ -998,4 +1009,5 @@ ALWAYS_INLINE void JIT::emitTagAsBoolImmediate(RegisterID reg) #endif // ENABLE(JIT) -#endif +#endif // JITInlines_h + diff --git a/Source/JavaScriptCore/jit/JITOpcodes.cpp b/Source/JavaScriptCore/jit/JITOpcodes.cpp index 4fb9d8cd5..9f0ce3a77 100644 --- a/Source/JavaScriptCore/jit/JITOpcodes.cpp +++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp @@ -29,9 +29,9 @@ #include "JIT.h" #include "Arguments.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "Heap.h" -#include "JITInlineMethods.h" +#include "JITInlines.h" #include "JITStubCall.h" #include "JSArray.h" #include "JSCell.h" @@ -1210,9 +1210,18 @@ void JIT::emit_op_convert_this(Instruction* currentInstruction) addSlowCase(branchPtr(Equal, Address(regT1, JSCell::structureOffset()), TrustedImmPtr(m_globalData->stringStructure.get()))); } -void JIT::emit_op_create_this(Instruction* currentInstruction) +void JIT::emit_op_get_callee(Instruction* currentInstruction) { + unsigned result = currentInstruction[1].u.operand; emitGetFromCallFrameHeaderPtr(JSStack::Callee, regT0); + emitValueProfilingSite(); + emitPutVirtualRegister(result); +} + +void JIT::emit_op_create_this(Instruction* currentInstruction) +{ + int callee = currentInstruction[2].u.operand; + emitGetVirtualRegister(callee, regT0); loadPtr(Address(regT0, JSFunction::offsetOfCachedInheritorID()), regT2); addSlowCase(branchTestPtr(Zero, regT2)); @@ -1952,6 +1961,7 @@ void JIT::emit_op_new_array(Instruction* currentInstruction) JITStubCall stubCall(this, cti_op_new_array); stubCall.addArgument(TrustedImm32(currentInstruction[2].u.operand)); stubCall.addArgument(TrustedImm32(currentInstruction[3].u.operand)); + stubCall.addArgument(TrustedImmPtr(currentInstruction[4].u.arrayAllocationProfile)); stubCall.call(currentInstruction[1].u.operand); } @@ -1963,6 +1973,7 @@ void JIT::emit_op_new_array_with_size(Instruction* currentInstruction) #else stubCall.addArgument(currentInstruction[2].u.operand); #endif + stubCall.addArgument(TrustedImmPtr(currentInstruction[3].u.arrayAllocationProfile)); stubCall.call(currentInstruction[1].u.operand); } @@ -1971,6 +1982,7 @@ void JIT::emit_op_new_array_buffer(Instruction* currentInstruction) JITStubCall stubCall(this, cti_op_new_array_buffer); stubCall.addArgument(TrustedImm32(currentInstruction[2].u.operand)); stubCall.addArgument(TrustedImm32(currentInstruction[3].u.operand)); + stubCall.addArgument(TrustedImmPtr(currentInstruction[4].u.arrayAllocationProfile)); stubCall.call(currentInstruction[1].u.operand); } diff --git a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp index 9c5d260ab..13daf962a 100644 --- a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp +++ b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp @@ -30,7 +30,7 @@ #if USE(JSVALUE32_64) #include "JIT.h" -#include "JITInlineMethods.h" +#include "JITInlines.h" #include "JITStubCall.h" #include "JSArray.h" #include "JSCell.h" @@ -1467,9 +1467,19 @@ void JIT::emit_op_init_lazy_reg(Instruction* currentInstruction) emitStore(dst, JSValue()); } -void JIT::emit_op_create_this(Instruction* currentInstruction) +void JIT::emit_op_get_callee(Instruction* currentInstruction) { + int dst = currentInstruction[1].u.operand; emitGetFromCallFrameHeaderPtr(JSStack::Callee, regT0); + move(TrustedImm32(JSValue::CellTag), regT1); + emitValueProfilingSite(); + emitStore(dst, regT1, regT0); +} + +void JIT::emit_op_create_this(Instruction* currentInstruction) +{ + int callee = currentInstruction[2].u.operand; + emitLoadPayload(callee, regT0); loadPtr(Address(regT0, JSFunction::offsetOfCachedInheritorID()), regT2); addSlowCase(branchTestPtr(Zero, regT2)); diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp index 6362598f4..57a5685eb 100644 --- a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp +++ b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp @@ -32,7 +32,7 @@ #include "GCAwareJITStubRoutine.h" #include "GetterSetter.h" #include "Interpreter.h" -#include "JITInlineMethods.h" +#include "JITInlines.h" #include "JITStubCall.h" #include "JSArray.h" #include "JSFunction.h" @@ -98,7 +98,7 @@ void JIT::emit_op_get_by_val(Instruction* currentInstruction) unsigned base = currentInstruction[2].u.operand; unsigned property = currentInstruction[3].u.operand; ArrayProfile* profile = currentInstruction[4].u.arrayProfile; - + emitGetVirtualRegisters(base, regT0, property, regT1); emitJumpSlowCaseIfNotImmediateInteger(regT1); @@ -120,6 +120,12 @@ void JIT::emit_op_get_by_val(Instruction* currentInstruction) JITArrayMode mode = chooseArrayMode(profile); switch (mode) { + case JITInt32: + slowCases = emitInt32GetByVal(currentInstruction, badType); + break; + case JITDouble: + slowCases = emitDoubleGetByVal(currentInstruction, badType); + break; case JITContiguous: slowCases = emitContiguousGetByVal(currentInstruction, badType); break; @@ -148,11 +154,26 @@ void JIT::emit_op_get_by_val(Instruction* currentInstruction) m_byValCompilationInfo.append(ByValCompilationInfo(m_bytecodeOffset, badType, mode, done)); } -JIT::JumpList JIT::emitContiguousGetByVal(Instruction*, PatchableJump& badType) +JIT::JumpList JIT::emitDoubleGetByVal(Instruction*, PatchableJump& badType) { JumpList slowCases; - badType = patchableBranch32(NotEqual, regT2, TrustedImm32(ContiguousShape)); + badType = patchableBranch32(NotEqual, regT2, TrustedImm32(DoubleShape)); + loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2); + slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, Butterfly::offsetOfPublicLength()))); + loadDouble(BaseIndex(regT2, regT1, TimesEight), fpRegT0); + slowCases.append(branchDouble(DoubleNotEqualOrUnordered, fpRegT0, fpRegT0)); + moveDoubleTo64(fpRegT0, regT0); + sub64(tagTypeNumberRegister, regT0); + + return slowCases; +} + +JIT::JumpList JIT::emitContiguousGetByVal(Instruction*, PatchableJump& badType, IndexingType expectedShape) +{ + JumpList slowCases; + + badType = patchableBranch32(NotEqual, regT2, TrustedImm32(expectedShape)); loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2); slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, Butterfly::offsetOfPublicLength()))); load64(BaseIndex(regT2, regT1, TimesEight), regT0); @@ -304,6 +325,12 @@ void JIT::emit_op_put_by_val(Instruction* currentInstruction) JITArrayMode mode = chooseArrayMode(profile); switch (mode) { + case JITInt32: + slowCases = emitInt32PutByVal(currentInstruction, badType); + break; + case JITDouble: + slowCases = emitDoublePutByVal(currentInstruction, badType); + break; case JITContiguous: slowCases = emitContiguousPutByVal(currentInstruction, badType); break; @@ -325,24 +352,48 @@ void JIT::emit_op_put_by_val(Instruction* currentInstruction) emitWriteBarrier(regT0, regT3, regT1, regT3, ShouldFilterImmediates, WriteBarrierForPropertyAccess); } -JIT::JumpList JIT::emitContiguousPutByVal(Instruction* currentInstruction, PatchableJump& badType) +JIT::JumpList JIT::emitGenericContiguousPutByVal(Instruction* currentInstruction, PatchableJump& badType, IndexingType indexingShape) { unsigned value = currentInstruction[3].u.operand; ArrayProfile* profile = currentInstruction[4].u.arrayProfile; - badType = patchableBranch32(NotEqual, regT2, TrustedImm32(ContiguousShape)); + JumpList slowCases; + + badType = patchableBranch32(NotEqual, regT2, TrustedImm32(indexingShape)); loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2); Jump outOfBounds = branch32(AboveOrEqual, regT1, Address(regT2, Butterfly::offsetOfPublicLength())); Label storeResult = label(); emitGetVirtualRegister(value, regT3); - store64(regT3, BaseIndex(regT2, regT1, TimesEight)); + switch (indexingShape) { + case Int32Shape: + slowCases.append(emitJumpIfNotImmediateInteger(regT3)); + store64(regT3, BaseIndex(regT2, regT1, TimesEight)); + break; + case DoubleShape: { + Jump notInt = emitJumpIfNotImmediateInteger(regT3); + convertInt32ToDouble(regT3, fpRegT0); + Jump ready = jump(); + notInt.link(this); + add64(tagTypeNumberRegister, regT3); + move64ToDouble(regT3, fpRegT0); + slowCases.append(branchDouble(DoubleNotEqualOrUnordered, fpRegT0, fpRegT0)); + ready.link(this); + storeDouble(fpRegT0, BaseIndex(regT2, regT1, TimesEight)); + break; + } + case ContiguousShape: + store64(regT3, BaseIndex(regT2, regT1, TimesEight)); + break; + default: + CRASH(); + break; + } Jump done = jump(); outOfBounds.link(this); - JumpList slowCases; slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, Butterfly::offsetOfVectorLength()))); emitArrayProfileStoreToHoleSpecialCase(profile); @@ -394,12 +445,23 @@ void JIT::emitSlow_op_put_by_val(Instruction* currentInstruction, Vector<SlowCas unsigned base = currentInstruction[1].u.operand; unsigned property = currentInstruction[2].u.operand; unsigned value = currentInstruction[3].u.operand; + ArrayProfile* profile = currentInstruction[4].u.arrayProfile; linkSlowCase(iter); // property int32 check linkSlowCaseIfNotJSCell(iter, base); // base cell check linkSlowCase(iter); // base not array check linkSlowCase(iter); // out of bounds + JITArrayMode mode = chooseArrayMode(profile); + switch (mode) { + case JITInt32: + case JITDouble: + linkSlowCase(iter); // value type check + break; + default: + break; + } + Label slowPath = label(); JITStubCall stubPutByValCall(this, cti_op_put_by_val); @@ -1312,6 +1374,12 @@ void JIT::privateCompileGetByVal(ByValInfo* byValInfo, ReturnAddressPtr returnAd JumpList slowCases; switch (arrayMode) { + case JITInt32: + slowCases = emitInt32GetByVal(currentInstruction, badType); + break; + case JITDouble: + slowCases = emitDoubleGetByVal(currentInstruction, badType); + break; case JITContiguous: slowCases = emitContiguousGetByVal(currentInstruction, badType); break; @@ -1375,6 +1443,12 @@ void JIT::privateCompilePutByVal(ByValInfo* byValInfo, ReturnAddressPtr returnAd JumpList slowCases; switch (arrayMode) { + case JITInt32: + slowCases = emitInt32PutByVal(currentInstruction, badType); + break; + case JITDouble: + slowCases = emitDoublePutByVal(currentInstruction, badType); + break; case JITContiguous: slowCases = emitContiguousPutByVal(currentInstruction, badType); break; diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp index 939766f04..be146a402 100644 --- a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp +++ b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp @@ -32,7 +32,7 @@ #include "CodeBlock.h" #include "GCAwareJITStubRoutine.h" #include "Interpreter.h" -#include "JITInlineMethods.h" +#include "JITInlines.h" #include "JITStubCall.h" #include "JSArray.h" #include "JSFunction.h" @@ -153,6 +153,12 @@ void JIT::emit_op_get_by_val(Instruction* currentInstruction) JITArrayMode mode = chooseArrayMode(profile); switch (mode) { + case JITInt32: + slowCases = emitInt32GetByVal(currentInstruction, badType); + break; + case JITDouble: + slowCases = emitDoubleGetByVal(currentInstruction, badType); + break; case JITContiguous: slowCases = emitContiguousGetByVal(currentInstruction, badType); break; @@ -181,11 +187,11 @@ void JIT::emit_op_get_by_val(Instruction* currentInstruction) m_byValCompilationInfo.append(ByValCompilationInfo(m_bytecodeOffset, badType, mode, done)); } -JIT::JumpList JIT::emitContiguousGetByVal(Instruction*, PatchableJump& badType) +JIT::JumpList JIT::emitContiguousGetByVal(Instruction*, PatchableJump& badType, IndexingType expectedShape) { JumpList slowCases; - badType = patchableBranch32(NotEqual, regT1, TrustedImm32(ContiguousShape)); + badType = patchableBranch32(NotEqual, regT1, TrustedImm32(expectedShape)); loadPtr(Address(regT0, JSObject::butterflyOffset()), regT3); slowCases.append(branch32(AboveOrEqual, regT2, Address(regT3, Butterfly::offsetOfPublicLength()))); @@ -197,6 +203,22 @@ JIT::JumpList JIT::emitContiguousGetByVal(Instruction*, PatchableJump& badType) return slowCases; } +JIT::JumpList JIT::emitDoubleGetByVal(Instruction*, PatchableJump& badType) +{ + JumpList slowCases; + + badType = patchableBranch32(NotEqual, regT1, TrustedImm32(DoubleShape)); + + loadPtr(Address(regT0, JSObject::butterflyOffset()), regT3); + slowCases.append(branch32(AboveOrEqual, regT2, Address(regT3, Butterfly::offsetOfPublicLength()))); + + loadDouble(BaseIndex(regT3, regT2, TimesEight), fpRegT0); + slowCases.append(branchDouble(DoubleNotEqualOrUnordered, fpRegT0, fpRegT0)); + moveDoubleToInts(fpRegT0, regT0, regT1); + + return slowCases; +} + JIT::JumpList JIT::emitArrayStorageGetByVal(Instruction*, PatchableJump& badType) { JumpList slowCases; @@ -270,6 +292,12 @@ void JIT::emit_op_put_by_val(Instruction* currentInstruction) JITArrayMode mode = chooseArrayMode(profile); switch (mode) { + case JITInt32: + slowCases = emitInt32PutByVal(currentInstruction, badType); + break; + case JITDouble: + slowCases = emitDoublePutByVal(currentInstruction, badType); + break; case JITContiguous: slowCases = emitContiguousPutByVal(currentInstruction, badType); break; @@ -289,7 +317,7 @@ void JIT::emit_op_put_by_val(Instruction* currentInstruction) m_byValCompilationInfo.append(ByValCompilationInfo(m_bytecodeOffset, badType, mode, done)); } -JIT::JumpList JIT::emitContiguousPutByVal(Instruction* currentInstruction, PatchableJump& badType) +JIT::JumpList JIT::emitGenericContiguousPutByVal(Instruction* currentInstruction, PatchableJump& badType, IndexingType indexingShape) { unsigned value = currentInstruction[3].u.operand; ArrayProfile* profile = currentInstruction[4].u.arrayProfile; @@ -303,8 +331,30 @@ JIT::JumpList JIT::emitContiguousPutByVal(Instruction* currentInstruction, Patch Label storeResult = label(); emitLoad(value, regT1, regT0); - store32(regT0, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); - store32(regT1, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + switch (indexingShape) { + case Int32Shape: + slowCases.append(branch32(NotEqual, regT1, TrustedImm32(JSValue::Int32Tag))); + // Fall through. + case ContiguousShape: + store32(regT0, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + store32(regT1, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + break; + case DoubleShape: { + Jump notInt = branch32(NotEqual, regT1, TrustedImm32(JSValue::Int32Tag)); + convertInt32ToDouble(regT0, fpRegT0); + Jump ready = jump(); + notInt.link(this); + moveIntsToDouble(regT0, regT1, fpRegT0, fpRegT1); + slowCases.append(branchDouble(DoubleNotEqualOrUnordered, fpRegT0, fpRegT0)); + ready.link(this); + storeDouble(fpRegT0, BaseIndex(regT3, regT2, TimesEight)); + break; + } + default: + CRASH(); + break; + } + Jump done = jump(); outOfBounds.link(this); @@ -364,12 +414,23 @@ void JIT::emitSlow_op_put_by_val(Instruction* currentInstruction, Vector<SlowCas unsigned base = currentInstruction[1].u.operand; unsigned property = currentInstruction[2].u.operand; unsigned value = currentInstruction[3].u.operand; + ArrayProfile* profile = currentInstruction[4].u.arrayProfile; linkSlowCase(iter); // property int32 check linkSlowCaseIfNotJSCell(iter, base); // base cell check linkSlowCase(iter); // base not array check linkSlowCase(iter); // out of bounds + JITArrayMode mode = chooseArrayMode(profile); + switch (mode) { + case JITInt32: + case JITDouble: + linkSlowCase(iter); // value type check + break; + default: + break; + } + Label slowPath = label(); JITStubCall stubPutByValCall(this, cti_op_put_by_val); diff --git a/Source/JavaScriptCore/jit/JITStubRoutine.h b/Source/JavaScriptCore/jit/JITStubRoutine.h index a46fcfd1a..020ef6907 100644 --- a/Source/JavaScriptCore/jit/JITStubRoutine.h +++ b/Source/JavaScriptCore/jit/JITStubRoutine.h @@ -150,11 +150,11 @@ protected: }; // Helper for the creation of simple stub routines that need no help from the GC. -#define FINALIZE_CODE_FOR_STUB(patchBuffer, dataLogArguments) \ - (adoptRef(new JITStubRoutine(FINALIZE_CODE((patchBuffer), dataLogArguments)))) +#define FINALIZE_CODE_FOR_STUB(patchBuffer, dataLogFArguments) \ + (adoptRef(new JITStubRoutine(FINALIZE_CODE((patchBuffer), dataLogFArguments)))) -#define FINALIZE_CODE_FOR_DFG_STUB(patchBuffer, dataLogArguments) \ - (adoptRef(new JITStubRoutine(FINALIZE_DFG_CODE((patchBuffer), dataLogArguments)))) +#define FINALIZE_CODE_FOR_DFG_STUB(patchBuffer, dataLogFArguments) \ + (adoptRef(new JITStubRoutine(FINALIZE_DFG_CODE((patchBuffer), dataLogFArguments)))) } // namespace JSC diff --git a/Source/JavaScriptCore/jit/JITStubs.cpp b/Source/JavaScriptCore/jit/JITStubs.cpp index 5ddb98dee..760ffd429 100644 --- a/Source/JavaScriptCore/jit/JITStubs.cpp +++ b/Source/JavaScriptCore/jit/JITStubs.cpp @@ -1808,12 +1808,12 @@ DEFINE_STUB_FUNCTION(void, optimize) unsigned bytecodeIndex = stackFrame.args[0].int32(); #if ENABLE(JIT_VERBOSE_OSR) - dataLog("%p: Entered optimize with bytecodeIndex = %u, executeCounter = %s, reoptimizationRetryCounter = %u, optimizationDelayCounter = %u, exitCounter = ", codeBlock, bytecodeIndex, codeBlock->jitExecuteCounter().status(), codeBlock->reoptimizationRetryCounter(), codeBlock->optimizationDelayCounter()); + dataLogF("%p: Entered optimize with bytecodeIndex = %u, executeCounter = %s, reoptimizationRetryCounter = %u, optimizationDelayCounter = %u, exitCounter = ", codeBlock, bytecodeIndex, codeBlock->jitExecuteCounter().status(), codeBlock->reoptimizationRetryCounter(), codeBlock->optimizationDelayCounter()); if (codeBlock->hasOptimizedReplacement()) - dataLog("%u", codeBlock->replacement()->osrExitCounter()); + dataLogF("%u", codeBlock->replacement()->osrExitCounter()); else - dataLog("N/A"); - dataLog("\n"); + dataLogF("N/A"); + dataLogF("\n"); #endif if (!codeBlock->checkIfOptimizationThresholdReached()) { @@ -1823,7 +1823,7 @@ DEFINE_STUB_FUNCTION(void, optimize) if (codeBlock->hasOptimizedReplacement()) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog("Considering OSR into %p(%p).\n", codeBlock, codeBlock->replacement()); + dataLogF("Considering OSR into %p(%p).\n", codeBlock, codeBlock->replacement()); #endif // If we have an optimized replacement, then it must be the case that we entered // cti_optimize from a loop. That's because is there's an optimized replacement, @@ -1840,7 +1840,7 @@ DEFINE_STUB_FUNCTION(void, optimize) // additional checking anyway, to reduce the amount of recompilation thrashing. if (codeBlock->replacement()->shouldReoptimizeFromLoopNow()) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog("Triggering reoptimization of %p(%p) (in loop).\n", codeBlock, codeBlock->replacement()); + dataLogF("Triggering reoptimization of %p(%p) (in loop).\n", codeBlock, codeBlock->replacement()); #endif codeBlock->reoptimize(); return; @@ -1848,7 +1848,7 @@ DEFINE_STUB_FUNCTION(void, optimize) } else { if (!codeBlock->shouldOptimizeNow()) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog("Delaying optimization for %p (in loop) because of insufficient profiling.\n", codeBlock); + dataLogF("Delaying optimization for %p (in loop) because of insufficient profiling.\n", codeBlock); #endif return; } @@ -1857,14 +1857,14 @@ DEFINE_STUB_FUNCTION(void, optimize) JSObject* error = codeBlock->compileOptimized(callFrame, scope, bytecodeIndex); #if ENABLE(JIT_VERBOSE_OSR) if (error) - dataLog("WARNING: optimized compilation failed.\n"); + dataLogF("WARNING: optimized compilation failed.\n"); #else UNUSED_PARAM(error); #endif if (codeBlock->replacement() == codeBlock) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog("Optimizing %p failed.\n", codeBlock); + dataLogF("Optimizing %p failed.\n", codeBlock); #endif ASSERT(codeBlock->getJITType() == JITCode::BaselineJIT); @@ -1877,8 +1877,13 @@ DEFINE_STUB_FUNCTION(void, optimize) ASSERT(optimizedCodeBlock->getJITType() == JITCode::DFGJIT); if (void* address = DFG::prepareOSREntry(callFrame, optimizedCodeBlock, bytecodeIndex)) { + if (Options::showDFGDisassembly()) { + dataLogF( + "Performing OSR from code block %p to code block %p, address %p to %p.\n", + codeBlock, optimizedCodeBlock, (STUB_RETURN_ADDRESS).value(), address); + } #if ENABLE(JIT_VERBOSE_OSR) - dataLog("Optimizing %p succeeded, performing OSR after a delay of %u.\n", codeBlock, codeBlock->optimizationDelayCounter()); + dataLogF("Optimizing %p succeeded, performing OSR after a delay of %u.\n", codeBlock, codeBlock->optimizationDelayCounter()); #endif codeBlock->optimizeSoon(); @@ -1887,7 +1892,7 @@ DEFINE_STUB_FUNCTION(void, optimize) } #if ENABLE(JIT_VERBOSE_OSR) - dataLog("Optimizing %p succeeded, OSR failed, after a delay of %u.\n", codeBlock, codeBlock->optimizationDelayCounter()); + dataLogF("Optimizing %p succeeded, OSR failed, after a delay of %u.\n", codeBlock, codeBlock->optimizationDelayCounter()); #endif // Count the OSR failure as a speculation failure. If this happens a lot, then @@ -1895,7 +1900,7 @@ DEFINE_STUB_FUNCTION(void, optimize) optimizedCodeBlock->countOSRExit(); #if ENABLE(JIT_VERBOSE_OSR) - dataLog("Encountered OSR failure into %p(%p).\n", codeBlock, codeBlock->replacement()); + dataLogF("Encountered OSR failure into %p(%p).\n", codeBlock, codeBlock->replacement()); #endif // We are a lot more conservative about triggering reoptimization after OSR failure than @@ -1908,7 +1913,7 @@ DEFINE_STUB_FUNCTION(void, optimize) // reoptimization trigger. if (optimizedCodeBlock->shouldReoptimizeNow()) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog("Triggering reoptimization of %p(%p) (after OSR fail).\n", codeBlock, codeBlock->replacement()); + dataLogF("Triggering reoptimization of %p(%p) (after OSR fail).\n", codeBlock, codeBlock->replacement()); #endif codeBlock->reoptimize(); return; @@ -2228,21 +2233,21 @@ DEFINE_STUB_FUNCTION(JSObject*, op_new_array) { STUB_INIT_STACK_FRAME(stackFrame); - return constructArray(stackFrame.callFrame, reinterpret_cast<JSValue*>(&stackFrame.callFrame->registers()[stackFrame.args[0].int32()]), stackFrame.args[1].int32()); + return constructArray(stackFrame.callFrame, stackFrame.args[2].arrayAllocationProfile(), reinterpret_cast<JSValue*>(&stackFrame.callFrame->registers()[stackFrame.args[0].int32()]), stackFrame.args[1].int32()); } DEFINE_STUB_FUNCTION(JSObject*, op_new_array_with_size) { STUB_INIT_STACK_FRAME(stackFrame); - return constructArrayWithSizeQuirk(stackFrame.callFrame, stackFrame.callFrame->lexicalGlobalObject(), stackFrame.args[0].jsValue()); + return constructArrayWithSizeQuirk(stackFrame.callFrame, stackFrame.args[1].arrayAllocationProfile(), stackFrame.callFrame->lexicalGlobalObject(), stackFrame.args[0].jsValue()); } DEFINE_STUB_FUNCTION(JSObject*, op_new_array_buffer) { STUB_INIT_STACK_FRAME(stackFrame); - return constructArray(stackFrame.callFrame, stackFrame.callFrame->codeBlock()->constantBuffer(stackFrame.args[0].int32()), stackFrame.args[1].int32()); + return constructArray(stackFrame.callFrame, stackFrame.args[2].arrayAllocationProfile(), stackFrame.callFrame->codeBlock()->constantBuffer(stackFrame.args[0].int32()), stackFrame.args[1].int32()); } DEFINE_STUB_FUNCTION(void, op_init_global_const_check) @@ -2470,7 +2475,7 @@ DEFINE_STUB_FUNCTION(void, op_put_by_val) JSValue baseValue = stackFrame.args[0].jsValue(); JSValue subscript = stackFrame.args[1].jsValue(); JSValue value = stackFrame.args[2].jsValue(); - + if (baseValue.isObject() && subscript.isInt32()) { // See if it's worth optimizing at all. JSObject* object = asObject(baseValue); diff --git a/Source/JavaScriptCore/jit/JITStubs.h b/Source/JavaScriptCore/jit/JITStubs.h index 5761236b1..3bf13bbdf 100644 --- a/Source/JavaScriptCore/jit/JITStubs.h +++ b/Source/JavaScriptCore/jit/JITStubs.h @@ -45,6 +45,7 @@ namespace JSC { struct StructureStubInfo; + class ArrayAllocationProfile; class CodeBlock; class ExecutablePool; class FunctionExecutable; @@ -85,6 +86,7 @@ namespace JSC { ReturnAddressPtr returnAddress() { return ReturnAddressPtr(asPointer); } ResolveOperations* resolveOperations() { return static_cast<ResolveOperations*>(asPointer); } PutToBaseOperation* putToBaseOperation() { return static_cast<PutToBaseOperation*>(asPointer); } + ArrayAllocationProfile* arrayAllocationProfile() { return static_cast<ArrayAllocationProfile*>(asPointer); } }; struct TrampolineStructure { diff --git a/Source/JavaScriptCore/jit/JumpReplacementWatchpoint.cpp b/Source/JavaScriptCore/jit/JumpReplacementWatchpoint.cpp index 26eae57be..13270d4d3 100644 --- a/Source/JavaScriptCore/jit/JumpReplacementWatchpoint.cpp +++ b/Source/JavaScriptCore/jit/JumpReplacementWatchpoint.cpp @@ -47,7 +47,7 @@ void JumpReplacementWatchpoint::fireInternal() void* source = bitwise_cast<void*>(m_source); void* destination = bitwise_cast<void*>(m_destination); if (Options::showDisassembly()) - dataLog("Firing jump replacement watchpoint from %p, to %p.\n", source, destination); + dataLogF("Firing jump replacement watchpoint from %p, to %p.\n", source, destination); MacroAssembler::replaceWithJump(CodeLocationLabel(source), CodeLocationLabel(destination)); if (isOnList()) remove(); diff --git a/Source/JavaScriptCore/jsc.cpp b/Source/JavaScriptCore/jsc.cpp index b8cf49da6..07a05b0c9 100644 --- a/Source/JavaScriptCore/jsc.cpp +++ b/Source/JavaScriptCore/jsc.cpp @@ -22,10 +22,10 @@ #include "config.h" -#include "ButterflyInlineMethods.h" +#include "ButterflyInlines.h" #include "BytecodeGenerator.h" #include "Completion.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "ExceptionHelpers.h" #include "HeapStatistics.h" #include "InitializeThreading.h" @@ -231,7 +231,7 @@ protected: addConstructableFunction(globalData, "Float32Array", constructJSFloat32Array, 1); addConstructableFunction(globalData, "Float64Array", constructJSFloat64Array, 1); - JSArray* array = constructEmptyArray(globalExec()); + JSArray* array = constructEmptyArray(globalExec(), 0); for (size_t i = 0; i < arguments.size(); ++i) array->putDirectIndex(globalExec(), i, jsString(globalExec(), arguments[i])); putDirect(globalData, Identifier(globalExec(), "arguments"), array); diff --git a/Source/JavaScriptCore/llint/LLIntData.cpp b/Source/JavaScriptCore/llint/LLIntData.cpp index eec376b37..90faff2ee 100644 --- a/Source/JavaScriptCore/llint/LLIntData.cpp +++ b/Source/JavaScriptCore/llint/LLIntData.cpp @@ -84,14 +84,14 @@ void Data::performAssertions(JSGlobalData& globalData) ASSERT(OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload) == 0); #endif #if USE(JSVALUE32_64) - ASSERT(JSValue::Int32Tag == -1); - ASSERT(JSValue::BooleanTag == -2); - ASSERT(JSValue::NullTag == -3); - ASSERT(JSValue::UndefinedTag == -4); - ASSERT(JSValue::CellTag == -5); - ASSERT(JSValue::EmptyValueTag == -6); - ASSERT(JSValue::DeletedValueTag == -7); - ASSERT(JSValue::LowestTag == -7); + ASSERT(JSValue::Int32Tag == static_cast<unsigned>(-1)); + ASSERT(JSValue::BooleanTag == static_cast<unsigned>(-2)); + ASSERT(JSValue::NullTag == static_cast<unsigned>(-3)); + ASSERT(JSValue::UndefinedTag == static_cast<unsigned>(-4)); + ASSERT(JSValue::CellTag == static_cast<unsigned>(-5)); + ASSERT(JSValue::EmptyValueTag == static_cast<unsigned>(-6)); + ASSERT(JSValue::DeletedValueTag == static_cast<unsigned>(-7)); + ASSERT(JSValue::LowestTag == static_cast<unsigned>(-7)); #else ASSERT(TagBitTypeOther == 0x2); ASSERT(TagBitBool == 0x4); diff --git a/Source/JavaScriptCore/llint/LLIntExceptions.cpp b/Source/JavaScriptCore/llint/LLIntExceptions.cpp index 80ca732ad..5e6bba365 100644 --- a/Source/JavaScriptCore/llint/LLIntExceptions.cpp +++ b/Source/JavaScriptCore/llint/LLIntExceptions.cpp @@ -50,7 +50,7 @@ void interpreterThrowInCaller(ExecState* exec, ReturnAddressPtr pc) JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); #if LLINT_SLOW_PATH_TRACING - dataLog("Throwing exception %s.\n", globalData->exception.description()); + dataLogF("Throwing exception %s.\n", globalData->exception.description()); #endif fixupPCforExceptionIfNeeded(exec); genericThrow( @@ -69,7 +69,7 @@ Instruction* returnToThrow(ExecState* exec, Instruction* pc) JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); #if LLINT_SLOW_PATH_TRACING - dataLog("Throwing exception %s (returnToThrow).\n", globalData->exception.description()); + dataLogF("Throwing exception %s (returnToThrow).\n", globalData->exception.description()); #endif fixupPCforExceptionIfNeeded(exec); genericThrow(globalData, exec, globalData->exception, pc - exec->codeBlock()->instructions().begin()); @@ -82,7 +82,7 @@ void* callToThrow(ExecState* exec, Instruction* pc) JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); #if LLINT_SLOW_PATH_TRACING - dataLog("Throwing exception %s (callToThrow).\n", globalData->exception.description()); + dataLogF("Throwing exception %s (callToThrow).\n", globalData->exception.description()); #endif fixupPCforExceptionIfNeeded(exec); genericThrow(globalData, exec, globalData->exception, pc - exec->codeBlock()->instructions().begin()); diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp index ba44bf404..584100e50 100644 --- a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp +++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp @@ -162,7 +162,7 @@ namespace JSC { namespace LLInt { extern "C" SlowPathReturnType llint_trace_operand(ExecState* exec, Instruction* pc, int fromWhere, int operand) { LLINT_BEGIN(); - dataLog("%p / %p: executing bc#%zu, op#%u: Trace(%d): %d: %d\n", + dataLogF("%p / %p: executing bc#%zu, op#%u: Trace(%d): %d: %d\n", exec->codeBlock(), exec, static_cast<intptr_t>(pc - exec->codeBlock()->instructions().begin()), @@ -184,7 +184,7 @@ extern "C" SlowPathReturnType llint_trace_value(ExecState* exec, Instruction* pc EncodedJSValue asValue; } u; u.asValue = JSValue::encode(value); - dataLog("%p / %p: executing bc#%zu, op#%u: Trace(%d): %d: %d: %08x:%08x: %s\n", + dataLogF("%p / %p: executing bc#%zu, op#%u: Trace(%d): %d: %d: %08x:%08x: %s\n", exec->codeBlock(), exec, static_cast<intptr_t>(pc - exec->codeBlock()->instructions().begin()), @@ -200,7 +200,7 @@ extern "C" SlowPathReturnType llint_trace_value(ExecState* exec, Instruction* pc LLINT_SLOW_PATH_DECL(trace_prologue) { - dataLog("%p / %p: in prologue.\n", exec->codeBlock(), exec); + dataLogF("%p / %p: in prologue.\n", exec->codeBlock(), exec); LLINT_END_IMPL(); } @@ -209,7 +209,7 @@ static void traceFunctionPrologue(ExecState* exec, const char* comment, CodeSpec JSFunction* callee = jsCast<JSFunction*>(exec->callee()); FunctionExecutable* executable = callee->jsExecutable(); CodeBlock* codeBlock = &executable->generatedBytecodeFor(kind); - dataLog("%p / %p: in %s of function %p, executable %p; numVars = %u, numParameters = %u, numCalleeRegisters = %u, caller = %p.\n", + dataLogF("%p / %p: in %s of function %p, executable %p; numVars = %u, numParameters = %u, numCalleeRegisters = %u, caller = %p.\n", codeBlock, exec, comment, callee, executable, codeBlock->m_numVars, codeBlock->numParameters(), codeBlock->m_numCalleeRegisters, exec->callerFrame()); @@ -241,22 +241,22 @@ LLINT_SLOW_PATH_DECL(trace_arityCheck_for_construct) LLINT_SLOW_PATH_DECL(trace) { - dataLog("%p / %p: executing bc#%zu, %s, scope %p\n", + dataLogF("%p / %p: executing bc#%zu, %s, scope %p\n", exec->codeBlock(), exec, static_cast<intptr_t>(pc - exec->codeBlock()->instructions().begin()), opcodeNames[exec->globalData().interpreter->getOpcodeID(pc[0].u.opcode)], exec->scope()); if (exec->globalData().interpreter->getOpcodeID(pc[0].u.opcode) == op_ret) { - dataLog("Will be returning to %p\n", exec->returnPC().value()); - dataLog("The new cfr will be %p\n", exec->callerFrame()); + dataLogF("Will be returning to %p\n", exec->returnPC().value()); + dataLogF("The new cfr will be %p\n", exec->callerFrame()); } LLINT_END_IMPL(); } LLINT_SLOW_PATH_DECL(special_trace) { - dataLog("%p / %p: executing special case bc#%zu, op#%u, return PC is %p\n", + dataLogF("%p / %p: executing special case bc#%zu, op#%u, return PC is %p\n", exec->codeBlock(), exec, static_cast<intptr_t>(pc - exec->codeBlock()->instructions().begin()), @@ -275,11 +275,11 @@ inline bool shouldJIT(ExecState* exec) // Returns true if we should try to OSR. inline bool jitCompileAndSetHeuristics(CodeBlock* codeBlock, ExecState* exec) { - codeBlock->updateAllPredictions(); + codeBlock->updateAllValueProfilePredictions(); if (!codeBlock->checkIfJITThresholdReached()) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" JIT threshold should be lifted.\n"); + dataLogF(" JIT threshold should be lifted.\n"); #endif return false; } @@ -288,19 +288,19 @@ inline bool jitCompileAndSetHeuristics(CodeBlock* codeBlock, ExecState* exec) switch (result) { case CodeBlock::AlreadyCompiled: #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" Code was already compiled.\n"); + dataLogF(" Code was already compiled.\n"); #endif codeBlock->jitSoon(); return true; case CodeBlock::CouldNotCompile: #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" JIT compilation failed.\n"); + dataLogF(" JIT compilation failed.\n"); #endif codeBlock->dontJITAnytimeSoon(); return false; case CodeBlock::CompiledSuccessfully: #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" JIT compilation successful.\n"); + dataLogF(" JIT compilation successful.\n"); #endif codeBlock->jitSoon(); return true; @@ -313,7 +313,7 @@ enum EntryKind { Prologue, ArityCheck }; static SlowPathReturnType entryOSR(ExecState* exec, Instruction*, CodeBlock* codeBlock, const char *name, EntryKind kind) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog("%p: Entered %s with executeCounter = %s\n", codeBlock, name, + dataLogF("%p: Entered %s with executeCounter = %s\n", codeBlock, name, codeBlock->llintExecuteCounter().status()); #else UNUSED_PARAM(name); @@ -362,7 +362,7 @@ LLINT_SLOW_PATH_DECL(loop_osr) CodeBlock* codeBlock = exec->codeBlock(); #if ENABLE(JIT_VERBOSE_OSR) - dataLog("%p: Entered loop_osr with executeCounter = %s\n", codeBlock, + dataLogF("%p: Entered loop_osr with executeCounter = %s\n", codeBlock, codeBlock->llintExecuteCounter().status()); #endif @@ -393,7 +393,7 @@ LLINT_SLOW_PATH_DECL(replace) CodeBlock* codeBlock = exec->codeBlock(); #if ENABLE(JIT_VERBOSE_OSR) - dataLog("%p: Entered replace with executeCounter = %s\n", codeBlock, + dataLogF("%p: Entered replace with executeCounter = %s\n", codeBlock, codeBlock->llintExecuteCounter().status()); #endif @@ -409,11 +409,11 @@ LLINT_SLOW_PATH_DECL(stack_check) { LLINT_BEGIN(); #if LLINT_SLOW_PATH_TRACING - dataLog("Checking stack height with exec = %p.\n", exec); - dataLog("CodeBlock = %p.\n", exec->codeBlock()); - dataLog("Num callee registers = %u.\n", exec->codeBlock()->m_numCalleeRegisters); - dataLog("Num vars = %u.\n", exec->codeBlock()->m_numVars); - dataLog("Current end is at %p.\n", exec->globalData().interpreter->stack().end()); + dataLogF("Checking stack height with exec = %p.\n", exec); + dataLogF("CodeBlock = %p.\n", exec->codeBlock()); + dataLogF("Num callee registers = %u.\n", exec->codeBlock()->m_numCalleeRegisters); + dataLogF("Num vars = %u.\n", exec->codeBlock()->m_numVars); + dataLogF("Current end is at %p.\n", exec->globalData().interpreter->stack().end()); #endif ASSERT(&exec->registers()[exec->codeBlock()->m_numCalleeRegisters] > exec->globalData().interpreter->stack().end()); if (UNLIKELY(!globalData.interpreter->stack().grow(&exec->registers()[exec->codeBlock()->m_numCalleeRegisters]))) { @@ -458,7 +458,7 @@ LLINT_SLOW_PATH_DECL(slow_path_create_activation) { LLINT_BEGIN(); #if LLINT_SLOW_PATH_TRACING - dataLog("Creating an activation, exec = %p!\n", exec); + dataLogF("Creating an activation, exec = %p!\n", exec); #endif JSActivation* activation = JSActivation::create(globalData, exec, exec->codeBlock()); exec->setScope(activation); @@ -478,7 +478,7 @@ LLINT_SLOW_PATH_DECL(slow_path_create_arguments) LLINT_SLOW_PATH_DECL(slow_path_create_this) { LLINT_BEGIN(); - JSFunction* constructor = jsCast<JSFunction*>(exec->callee()); + JSFunction* constructor = jsCast<JSFunction*>(LLINT_OP(2).jsValue().asCell()); #if !ASSERT_DISABLED ConstructData constructData; @@ -510,19 +510,19 @@ LLINT_SLOW_PATH_DECL(slow_path_new_object) LLINT_SLOW_PATH_DECL(slow_path_new_array) { LLINT_BEGIN(); - LLINT_RETURN(constructArray(exec, bitwise_cast<JSValue*>(&LLINT_OP(2)), pc[3].u.operand)); + LLINT_RETURN(constructArray(exec, pc[4].u.arrayAllocationProfile, bitwise_cast<JSValue*>(&LLINT_OP(2)), pc[3].u.operand)); } LLINT_SLOW_PATH_DECL(slow_path_new_array_with_size) { LLINT_BEGIN(); - LLINT_RETURN(constructArrayWithSizeQuirk(exec, exec->lexicalGlobalObject(), LLINT_OP_C(2).jsValue())); + LLINT_RETURN(constructArrayWithSizeQuirk(exec, pc[3].u.arrayAllocationProfile, exec->lexicalGlobalObject(), LLINT_OP_C(2).jsValue())); } LLINT_SLOW_PATH_DECL(slow_path_new_array_buffer) { LLINT_BEGIN(); - LLINT_RETURN(constructArray(exec, exec->codeBlock()->constantBuffer(pc[2].u.operand), pc[3].u.operand)); + LLINT_RETURN(constructArray(exec, pc[4].u.arrayAllocationProfile, exec->codeBlock()->constantBuffer(pc[2].u.operand), pc[3].u.operand)); } LLINT_SLOW_PATH_DECL(slow_path_new_regexp) @@ -635,8 +635,8 @@ LLINT_SLOW_PATH_DECL(slow_path_add) JSValue v2 = LLINT_OP_C(3).jsValue(); #if LLINT_SLOW_PATH_TRACING - dataLog("Trying to add %s", v1.description()); - dataLog(" to %s.\n", v2.description()); + dataLogF("Trying to add %s", v1.description()); + dataLogF(" to %s.\n", v2.description()); #endif if (v1.isString() && !v2.isObject()) @@ -1328,7 +1328,7 @@ LLINT_SLOW_PATH_DECL(slow_path_new_func) || !codeBlock->needsFullScopeChain() || exec->uncheckedR(codeBlock->activationRegister()).jsValue()); #if LLINT_SLOW_PATH_TRACING - dataLog("Creating function!\n"); + dataLogF("Creating function!\n"); #endif LLINT_RETURN(JSFunction::create(exec, codeBlock->functionDecl(pc[2].u.operand), exec->scope())); } @@ -1367,7 +1367,7 @@ static SlowPathReturnType handleHostCall(ExecState* execCallee, Instruction* pc, } #if LLINT_SLOW_PATH_TRACING - dataLog("Call callee is not a function: %s\n", callee.description()); + dataLogF("Call callee is not a function: %s\n", callee.description()); #endif ASSERT(callType == CallTypeNone); @@ -1390,7 +1390,7 @@ static SlowPathReturnType handleHostCall(ExecState* execCallee, Instruction* pc, } #if LLINT_SLOW_PATH_TRACING - dataLog("Constructor callee is not a function: %s\n", callee.description()); + dataLogF("Constructor callee is not a function: %s\n", callee.description()); #endif ASSERT(constructType == ConstructTypeNone); @@ -1400,7 +1400,7 @@ static SlowPathReturnType handleHostCall(ExecState* execCallee, Instruction* pc, inline SlowPathReturnType setUpCall(ExecState* execCallee, Instruction* pc, CodeSpecializationKind kind, JSValue calleeAsValue, LLIntCallLinkInfo* callLinkInfo = 0) { #if LLINT_SLOW_PATH_TRACING - dataLog("Performing call with recorded PC = %p\n", execCallee->callerFrame()->currentVPC()); + dataLogF("Performing call with recorded PC = %p\n", execCallee->callerFrame()->currentVPC()); #endif JSCell* calleeAsFunctionCell = getJSFunction(calleeAsValue); diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm index ba5b67df4..00d5c4f6f 100644 --- a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm +++ b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm @@ -88,10 +88,13 @@ else end # Constant for reasoning about butterflies. -const IsArray = 1 -const IndexingShapeMask = 30 -const ContiguousShape = 26 -const ArrayStorageShape = 28 +const IsArray = 1 +const IndexingShapeMask = 30 +const NoIndexingShape = 0 +const Int32Shape = 20 +const DoubleShape = 22 +const ContiguousShape = 26 +const ArrayStorageShape = 28 const SlowPutArrayStorageShape = 30 # Type constants. @@ -462,19 +465,19 @@ end _llint_op_new_array: traceExecution() callSlowPath(_llint_slow_path_new_array) - dispatch(4) + dispatch(5) _llint_op_new_array_with_size: traceExecution() callSlowPath(_llint_slow_path_new_array_with_size) - dispatch(3) + dispatch(4) _llint_op_new_array_buffer: traceExecution() callSlowPath(_llint_slow_path_new_array_buffer) - dispatch(4) + dispatch(5) _llint_op_new_regexp: diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm index ffb146247..8d5cdf108 100644 --- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm +++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm @@ -349,18 +349,30 @@ _llint_op_create_arguments: _llint_op_create_this: traceExecution() - loadp Callee[cfr], t0 + loadi 8[PC], t0 + loadp PayloadOffset[cfr, t0, 8], t0 loadp JSFunction::m_cachedInheritorID[t0], t2 btpz t2, .opCreateThisSlow allocateBasicJSObject(JSFinalObjectSizeClassIndex, t2, t0, t1, t3, .opCreateThisSlow) loadi 4[PC], t1 storei CellTag, TagOffset[cfr, t1, 8] storei t0, PayloadOffset[cfr, t1, 8] - dispatch(2) + dispatch(3) .opCreateThisSlow: callSlowPath(_llint_slow_path_create_this) - dispatch(2) + dispatch(3) + + +_llint_op_get_callee: + traceExecution() + loadi 4[PC], t0 + loadp PayloadOffset + Callee[cfr], t1 + loadp 8[PC], t2 + valueProfile(CellTag, t1, t2) + storei CellTag, TagOffset[cfr, t0, 8] + storei t1, PayloadOffset[cfr, t0, 8] + dispatch(3) _llint_op_convert_this: @@ -1185,7 +1197,9 @@ _llint_op_get_by_val: loadConstantOrVariablePayload(t3, Int32Tag, t1, .opGetByValSlow) loadp JSObject::m_butterfly[t0], t3 andi IndexingShapeMask, t2 + bieq t2, Int32Shape, .opGetByValIsContiguous bineq t2, ContiguousShape, .opGetByValNotContiguous +.opGetByValIsContiguous: biaeq t1, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t3], .opGetByValSlow loadi TagOffset[t3, t1, 8], t2 @@ -1193,6 +1207,16 @@ _llint_op_get_by_val: jmp .opGetByValDone .opGetByValNotContiguous: + bineq t2, DoubleShape, .opGetByValNotDouble + biaeq t1, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t3], .opGetByValSlow + loadd [t3, t1, 8], ft0 + bdnequn ft0, ft0, .opGetByValSlow + # FIXME: This could be massively optimized. + fd2ii ft0, t1, t2 + loadi 4[PC], t0 + jmp .opGetByValNotEmpty + +.opGetByValNotDouble: subi ArrayStorageShape, t2 bia t2, SlowPutArrayStorageShape - ArrayStorageShape, .opGetByValSlow biaeq t1, -sizeof IndexingHeader + IndexingHeader::m_vectorLength[t3], .opGetByValSlow @@ -1202,6 +1226,7 @@ _llint_op_get_by_val: .opGetByValDone: loadi 4[PC], t0 bieq t2, EmptyValueTag, .opGetByValSlow +.opGetByValNotEmpty: storei t2, TagOffset[cfr, t0, 8] storei t1, PayloadOffset[cfr, t0, 8] loadi 20[PC], t0 @@ -1270,6 +1295,24 @@ _llint_op_get_by_pname: dispatch(7) +macro contiguousPutByVal(storeCallback) + biaeq t3, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t0], .outOfBounds +.storeResult: + loadi 12[PC], t2 + storeCallback(t2, t1, t0, t3) + dispatch(5) + +.outOfBounds: + biaeq t3, -sizeof IndexingHeader + IndexingHeader::m_vectorLength[t0], .opPutByValSlow + if VALUE_PROFILER + loadp 16[PC], t2 + storeb 1, ArrayProfile::m_mayStoreToHole[t2] + end + addi 1, t3, t2 + storei t2, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t0] + jmp .storeResult +end + _llint_op_put_by_val: traceExecution() loadi 4[PC], t0 @@ -1281,26 +1324,42 @@ _llint_op_put_by_val: loadConstantOrVariablePayload(t0, Int32Tag, t3, .opPutByValSlow) loadp JSObject::m_butterfly[t1], t0 andi IndexingShapeMask, t2 - bineq t2, ContiguousShape, .opPutByValNotContiguous + bineq t2, Int32Shape, .opPutByValNotInt32 + contiguousPutByVal( + macro (operand, scratch, base, index) + loadConstantOrVariablePayload(operand, Int32Tag, scratch, .opPutByValSlow) + storei Int32Tag, TagOffset[base, index, 8] + storei scratch, PayloadOffset[base, index, 8] + end) - biaeq t3, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t0], .opPutByValContiguousOutOfBounds -.opPutByValContiguousStoreResult: - loadi 12[PC], t2 - loadConstantOrVariable2Reg(t2, t1, t2) - writeBarrier(t1, t2) - storei t1, TagOffset[t0, t3, 8] - storei t2, PayloadOffset[t0, t3, 8] - dispatch(5) +.opPutByValNotInt32: + bineq t2, DoubleShape, .opPutByValNotDouble + contiguousPutByVal( + macro (operand, scratch, base, index) + const tag = scratch + const payload = operand + loadConstantOrVariable2Reg(operand, tag, payload) + bineq tag, Int32Tag, .notInt + ci2d payload, ft0 + jmp .ready + .notInt: + fii2d payload, tag, ft0 + bdnequn ft0, ft0, .opPutByValSlow + .ready: + stored ft0, [base, index, 8] + end) -.opPutByValContiguousOutOfBounds: - biaeq t3, -sizeof IndexingHeader + IndexingHeader::m_vectorLength[t0], .opPutByValSlow - if VALUE_PROFILER - loadp 16[PC], t1 - storeb 1, ArrayProfile::m_mayStoreToHole[t1] - end - addi 1, t3, t2 - storei t2, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t0] - jmp .opPutByValContiguousStoreResult +.opPutByValNotDouble: + bineq t2, ContiguousShape, .opPutByValNotContiguous + contiguousPutByVal( + macro (operand, scratch, base, index) + const tag = scratch + const payload = operand + loadConstantOrVariable2Reg(operand, tag, payload) + writeBarrier(tag, payload) + storei tag, TagOffset[base, index, 8] + storei payload, PayloadOffset[base, index, 8] + end) .opPutByValNotContiguous: bineq t2, ArrayStorageShape, .opPutByValSlow diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm index c9900b343..ed6799ef3 100644 --- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm +++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm @@ -241,17 +241,28 @@ _llint_op_create_arguments: _llint_op_create_this: traceExecution() - loadp Callee[cfr], t0 + loadisFromInstruction(2, t0) + loadp [cfr, t0, 8], t0 loadp JSFunction::m_cachedInheritorID[t0], t2 btpz t2, .opCreateThisSlow allocateBasicJSObject(JSFinalObjectSizeClassIndex, t2, t0, t1, t3, .opCreateThisSlow) loadisFromInstruction(1, t1) storeq t0, [cfr, t1, 8] - dispatch(2) + dispatch(3) .opCreateThisSlow: callSlowPath(_llint_slow_path_create_this) - dispatch(2) + dispatch(3) + + +_llint_op_get_callee: + traceExecution() + loadisFromInstruction(1, t0) + loadpFromInstruction(2, t2) + loadp Callee[cfr], t1 + valueProfile(t1, t2) + storep t1, [cfr, t0, 8] + dispatch(3) _llint_op_convert_this: @@ -1025,7 +1036,9 @@ _llint_op_get_by_val: sxi2q t1, t1 loadp JSObject::m_butterfly[t0], t3 andi IndexingShapeMask, t2 + bieq t2, Int32Shape, .opGetByValIsContiguous bineq t2, ContiguousShape, .opGetByValNotContiguous +.opGetByValIsContiguous: biaeq t1, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t3], .opGetByValSlow loadisFromInstruction(1, t0) @@ -1034,6 +1047,16 @@ _llint_op_get_by_val: jmp .opGetByValDone .opGetByValNotContiguous: + bineq t2, DoubleShape, .opGetByValNotDouble + biaeq t1, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t3], .opGetByValSlow + loadis 8[PB, PC, 8], t0 + loadd [t3, t1, 8], ft0 + bdnequn ft0, ft0, .opGetByValSlow + fd2q ft0, t2 + subq tagTypeNumber, t2 + jmp .opGetByValDone + +.opGetByValNotDouble: subi ArrayStorageShape, t2 bia t2, SlowPutArrayStorageShape - ArrayStorageShape, .opGetByValSlow biaeq t1, -sizeof IndexingHeader + IndexingHeader::m_vectorLength[t3], .opGetByValSlow @@ -1109,6 +1132,24 @@ _llint_op_get_by_pname: dispatch(7) +macro contiguousPutByVal(storeCallback) + biaeq t3, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t0], .outOfBounds +.storeResult: + loadisFromInstruction(3, t2) + storeCallback(t2, t1, [t0, t3, 8]) + dispatch(5) + +.outOfBounds: + biaeq t3, -sizeof IndexingHeader + IndexingHeader::m_vectorLength[t0], .opPutByValSlow + if VALUE_PROFILER + loadp 32[PB, PC, 8], t2 + storeb 1, ArrayProfile::m_mayStoreToHole[t2] + end + addi 1, t3, t2 + storei t2, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t0] + jmp .storeResult +end + _llint_op_put_by_val: traceExecution() loadisFromInstruction(1, t0) @@ -1121,25 +1162,38 @@ _llint_op_put_by_val: sxi2q t3, t3 loadp JSObject::m_butterfly[t1], t0 andi IndexingShapeMask, t2 - bineq t2, ContiguousShape, .opPutByValNotContiguous - - biaeq t3, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t0], .opPutByValContiguousOutOfBounds -.opPutByValContiguousStoreResult: - loadisFromInstruction(3, t2) - loadConstantOrVariable(t2, t1) - writeBarrier(t1) - storeq t1, [t0, t3, 8] - dispatch(5) + bineq t2, Int32Shape, .opPutByValNotInt32 + contiguousPutByVal( + macro (operand, scratch, address) + loadConstantOrVariable(operand, scratch) + bpb scratch, tagTypeNumber, .opPutByValSlow + storep scratch, address + end) -.opPutByValContiguousOutOfBounds: - biaeq t3, -sizeof IndexingHeader + IndexingHeader::m_vectorLength[t0], .opPutByValSlow - if VALUE_PROFILER - loadpFromInstruction(4, t2) - storeb 1, ArrayProfile::m_mayStoreToHole[t2] - end - addi 1, t3, t2 - storei t2, -sizeof IndexingHeader + IndexingHeader::m_publicLength[t0] - jmp .opPutByValContiguousStoreResult +.opPutByValNotInt32: + bineq t2, DoubleShape, .opPutByValNotDouble + contiguousPutByVal( + macro (operand, scratch, address) + loadConstantOrVariable(operand, scratch) + bqb scratch, tagTypeNumber, .notInt + ci2d scratch, ft0 + jmp .ready + .notInt: + addp tagTypeNumber, scratch + fq2d scratch, ft0 + bdnequn ft0, ft0, .opPutByValSlow + .ready: + stored ft0, address + end) + +.opPutByValNotDouble: + bineq t2, ContiguousShape, .opPutByValNotContiguous + contiguousPutByVal( + macro (operand, scratch, address) + loadConstantOrVariable(operand, scratch) + writeBarrier(scratch) + storep scratch, address + end) .opPutByValNotContiguous: bineq t2, ArrayStorageShape, .opPutByValSlow diff --git a/Source/JavaScriptCore/offlineasm/x86.rb b/Source/JavaScriptCore/offlineasm/x86.rb index 67cbd14b0..f78b43912 100644 --- a/Source/JavaScriptCore/offlineasm/x86.rb +++ b/Source/JavaScriptCore/offlineasm/x86.rb @@ -764,11 +764,16 @@ class Instruction when "ci2d" $asm.puts "cvtsi2sd #{operands[0].x86Operand(:int)}, #{operands[1].x86Operand(:double)}" when "bdeq" - isUnordered = LocalLabel.unique("bdeq") $asm.puts "ucomisd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}" - $asm.puts "jp #{LabelReference.new(codeOrigin, isUnordered).asmLabel}" - $asm.puts "je #{LabelReference.new(codeOrigin, operands[2]).asmLabel}" - isUnordered.lower("X86") + if operands[0] == operands[1] + # This is just a jump ordered, which is a jnp. + $asm.puts "jnp #{operands[2].asmLabel}" + else + isUnordered = LocalLabel.unique("bdeq") + $asm.puts "jp #{LabelReference.new(codeOrigin, isUnordered).asmLabel}" + $asm.puts "je #{LabelReference.new(codeOrigin, operands[2]).asmLabel}" + isUnordered.lower("X86") + end when "bdneq" handleX86DoubleBranch("jne", :normal) when "bdgt" @@ -782,14 +787,19 @@ class Instruction when "bdequn" handleX86DoubleBranch("je", :normal) when "bdnequn" - isUnordered = LocalLabel.unique("bdnequn") - isEqual = LocalLabel.unique("bdnequn") $asm.puts "ucomisd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}" - $asm.puts "jp #{LabelReference.new(codeOrigin, isUnordered).asmLabel}" - $asm.puts "je #{LabelReference.new(codeOrigin, isEqual).asmLabel}" - isUnordered.lower("X86") - $asm.puts "jmp #{operands[2].asmLabel}" - isEqual.lower("X86") + if operands[0] == operands[1] + # This is just a jump unordered, which is a jp. + $asm.puts "jp #{operands[2].asmLabel}" + else + isUnordered = LocalLabel.unique("bdnequn") + isEqual = LocalLabel.unique("bdnequn") + $asm.puts "jp #{LabelReference.new(codeOrigin, isUnordered).asmLabel}" + $asm.puts "je #{LabelReference.new(codeOrigin, isEqual).asmLabel}" + isUnordered.lower("X86") + $asm.puts "jmp #{operands[2].asmLabel}" + isEqual.lower("X86") + end when "bdgtun" handleX86DoubleBranch("jb", :reverse) when "bdgtequn" @@ -1115,7 +1125,7 @@ class Instruction $asm.puts "movd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:int)}" $asm.puts "movsd #{operands[0].x86Operand(:double)}, %xmm7" $asm.puts "psrlq $32, %xmm7" - $asm.puts "movsd %xmm7, #{operands[2].x86Operand(:int)}" + $asm.puts "movd %xmm7, #{operands[2].x86Operand(:int)}" when "fq2d" $asm.puts "movd #{operands[0].x86Operand(:quad)}, #{operands[1].x86Operand(:double)}" when "fd2q" diff --git a/Source/JavaScriptCore/profiler/Profile.cpp b/Source/JavaScriptCore/profiler/Profile.cpp index a641580ec..2274889a1 100644 --- a/Source/JavaScriptCore/profiler/Profile.cpp +++ b/Source/JavaScriptCore/profiler/Profile.cpp @@ -103,7 +103,7 @@ void Profile::restoreAll() #ifndef NDEBUG void Profile::debugPrintData() const { - dataLog("Call graph:\n"); + dataLogF("Call graph:\n"); m_head->debugPrintData(0); } @@ -119,18 +119,18 @@ void Profile::debugPrintDataSampleStyle() const typedef Vector<NameCountPair> NameCountPairVector; FunctionCallHashCount countedFunctions; - dataLog("Call graph:\n"); + dataLogF("Call graph:\n"); m_head->debugPrintDataSampleStyle(0, countedFunctions); - dataLog("\nTotal number in stack:\n"); + dataLogF("\nTotal number in stack:\n"); NameCountPairVector sortedFunctions(countedFunctions.size()); copyToVector(countedFunctions, sortedFunctions); std::sort(sortedFunctions.begin(), sortedFunctions.end(), functionNameCountPairComparator); for (NameCountPairVector::iterator it = sortedFunctions.begin(); it != sortedFunctions.end(); ++it) - dataLog(" %-12d%s\n", (*it).value, String((*it).key).utf8().data()); + dataLogF(" %-12d%s\n", (*it).value, String((*it).key).utf8().data()); - dataLog("\nSort by top of stack, same collapsed (when >= 5):\n"); + dataLogF("\nSort by top of stack, same collapsed (when >= 5):\n"); } #endif diff --git a/Source/JavaScriptCore/profiler/ProfileNode.cpp b/Source/JavaScriptCore/profiler/ProfileNode.cpp index ab43d1511..6c36e47d0 100644 --- a/Source/JavaScriptCore/profiler/ProfileNode.cpp +++ b/Source/JavaScriptCore/profiler/ProfileNode.cpp @@ -294,9 +294,9 @@ void ProfileNode::debugPrintData(int indentLevel) const { // Print function names for (int i = 0; i < indentLevel; ++i) - dataLog(" "); + dataLogF(" "); - dataLog("Function Name %s %d SelfTime %.3fms/%.3f%% TotalTime %.3fms/%.3f%% VSelf %.3fms VTotal %.3fms Visible %s Next Sibling %s\n", + dataLogF("Function Name %s %d SelfTime %.3fms/%.3f%% TotalTime %.3fms/%.3f%% VSelf %.3fms VTotal %.3fms Visible %s Next Sibling %s\n", functionName().utf8().data(), m_numberOfCalls, m_actualSelfTime, selfPercent(), m_actualTotalTime, totalPercent(), m_visibleSelfTime, m_visibleTotalTime, @@ -313,20 +313,20 @@ void ProfileNode::debugPrintData(int indentLevel) const // print the profiled data in a format that matches the tool sample's output. double ProfileNode::debugPrintDataSampleStyle(int indentLevel, FunctionCallHashCount& countedFunctions) const { - dataLog(" "); + dataLogF(" "); // Print function names const char* name = functionName().utf8().data(); double sampleCount = m_actualTotalTime * 1000; if (indentLevel) { for (int i = 0; i < indentLevel; ++i) - dataLog(" "); + dataLogF(" "); countedFunctions.add(functionName().impl()); - dataLog("%.0f %s\n", sampleCount ? sampleCount : 1, name); + dataLogF("%.0f %s\n", sampleCount ? sampleCount : 1, name); } else - dataLog("%s\n", name); + dataLogF("%s\n", name); ++indentLevel; @@ -338,11 +338,11 @@ double ProfileNode::debugPrintDataSampleStyle(int indentLevel, FunctionCallHashC sumOfChildrensCount *= 1000; // // Print remainder of samples to match sample's output if (sumOfChildrensCount < sampleCount) { - dataLog(" "); + dataLogF(" "); while (indentLevel--) - dataLog(" "); + dataLogF(" "); - dataLog("%.0f %s\n", sampleCount - sumOfChildrensCount, functionName().utf8().data()); + dataLogF("%.0f %s\n", sampleCount - sumOfChildrensCount, functionName().utf8().data()); } return m_actualTotalTime; diff --git a/Source/JavaScriptCore/profiler/ProfileNode.h b/Source/JavaScriptCore/profiler/ProfileNode.h index 26000a827..f5fef86f8 100644 --- a/Source/JavaScriptCore/profiler/ProfileNode.h +++ b/Source/JavaScriptCore/profiler/ProfileNode.h @@ -64,6 +64,7 @@ namespace JSC { // CallIdentifier members ExecState* callerCallFrame() const { return m_callerCallFrame; } const CallIdentifier& callIdentifier() const { return m_callIdentifier; } + unsigned long callUID() const { return m_callIdentifier.hash(); }; const String& functionName() const { return m_callIdentifier.m_name; } const String& url() const { return m_callIdentifier.m_url; } unsigned lineNumber() const { return m_callIdentifier.m_lineNumber; } diff --git a/Source/JavaScriptCore/runtime/Arguments.h b/Source/JavaScriptCore/runtime/Arguments.h index 7961d4bc8..8ae991422 100644 --- a/Source/JavaScriptCore/runtime/Arguments.h +++ b/Source/JavaScriptCore/runtime/Arguments.h @@ -34,246 +34,246 @@ namespace JSC { - class Arguments : public JSDestructibleObject { - friend class JIT; - friend class DFG::SpeculativeJIT; - public: - typedef JSDestructibleObject Base; - - static Arguments* create(JSGlobalData& globalData, CallFrame* callFrame) - { - Arguments* arguments = new (NotNull, allocateCell<Arguments>(globalData.heap)) Arguments(callFrame); - arguments->finishCreation(callFrame); - return arguments; - } - - static Arguments* create(JSGlobalData& globalData, CallFrame* callFrame, InlineCallFrame* inlineCallFrame) - { - Arguments* arguments = new (NotNull, allocateCell<Arguments>(globalData.heap)) Arguments(callFrame); - arguments->finishCreation(callFrame, inlineCallFrame); - return arguments; - } - - enum { MaxArguments = 0x10000 }; - - private: - enum NoParametersType { NoParameters }; - - Arguments(CallFrame*); - Arguments(CallFrame*, NoParametersType); - - void tearOffForInlineCallFrame(JSGlobalData& globalData, Register*, InlineCallFrame*); +class Arguments : public JSDestructibleObject { + friend class JIT; + friend class DFG::SpeculativeJIT; +public: + typedef JSDestructibleObject Base; - public: - static const ClassInfo s_info; - - static void visitChildren(JSCell*, SlotVisitor&); - - void fillArgList(ExecState*, MarkedArgumentBuffer&); - - uint32_t length(ExecState* exec) const - { - if (UNLIKELY(m_overrodeLength)) - return get(exec, exec->propertyNames().length).toUInt32(exec); - return m_numArguments; - } - - void copyToArguments(ExecState*, CallFrame*, uint32_t length); - void tearOff(CallFrame*); - void tearOff(CallFrame*, InlineCallFrame*); - bool isTornOff() const { return m_registerArray; } - void didTearOffActivation(ExecState*, JSActivation*); - - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) - { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); - } - - protected: - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; - - void finishCreation(CallFrame*); - void finishCreation(CallFrame*, InlineCallFrame*); - - private: - static void destroy(JSCell*); - static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); - static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); - static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); - static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); - static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); - static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); - static bool deleteProperty(JSCell*, ExecState*, PropertyName); - static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); - static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow); - void createStrictModeCallerIfNecessary(ExecState*); - void createStrictModeCalleeIfNecessary(ExecState*); - - bool isArgument(size_t); - bool trySetArgument(JSGlobalData&, size_t argument, JSValue); - JSValue tryGetArgument(size_t argument); - bool isDeletedArgument(size_t); - bool tryDeleteArgument(size_t); - WriteBarrierBase<Unknown>& argument(size_t); - void allocateSlowArguments(); - - void init(CallFrame*); - - WriteBarrier<JSActivation> m_activation; - - unsigned m_numArguments; - - // We make these full byte booleans to make them easy to test from the JIT, - // and because even if they were single-bit booleans we still wouldn't save - // any space. - bool m_overrodeLength; - bool m_overrodeCallee; - bool m_overrodeCaller; - bool m_isStrictMode; - - WriteBarrierBase<Unknown>* m_registers; - OwnArrayPtr<WriteBarrier<Unknown> > m_registerArray; - - OwnArrayPtr<SlowArgument> m_slowArguments; - - WriteBarrier<JSFunction> m_callee; - }; - - Arguments* asArguments(JSValue); - - inline Arguments* asArguments(JSValue value) + static Arguments* create(JSGlobalData& globalData, CallFrame* callFrame) { - ASSERT(asObject(value)->inherits(&Arguments::s_info)); - return static_cast<Arguments*>(asObject(value)); + Arguments* arguments = new (NotNull, allocateCell<Arguments>(globalData.heap)) Arguments(callFrame); + arguments->finishCreation(callFrame); + return arguments; } - - inline Arguments::Arguments(CallFrame* callFrame) - : JSDestructibleObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) + + static Arguments* create(JSGlobalData& globalData, CallFrame* callFrame, InlineCallFrame* inlineCallFrame) { + Arguments* arguments = new (NotNull, allocateCell<Arguments>(globalData.heap)) Arguments(callFrame); + arguments->finishCreation(callFrame, inlineCallFrame); + return arguments; } - inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) - : JSDestructibleObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) - { - } + enum { MaxArguments = 0x10000 }; - inline void Arguments::allocateSlowArguments() - { - if (m_slowArguments) - return; - m_slowArguments = adoptArrayPtr(new SlowArgument[m_numArguments]); - for (size_t i = 0; i < m_numArguments; ++i) { - ASSERT(m_slowArguments[i].status == SlowArgument::Normal); - m_slowArguments[i].index = CallFrame::argumentOffset(i); - } - } +private: + enum NoParametersType { NoParameters }; + + Arguments(CallFrame*); + Arguments(CallFrame*, NoParametersType); + + void tearOffForInlineCallFrame(JSGlobalData& globalData, Register*, InlineCallFrame*); - inline bool Arguments::tryDeleteArgument(size_t argument) - { - if (!isArgument(argument)) - return false; - allocateSlowArguments(); - m_slowArguments[argument].status = SlowArgument::Deleted; - return true; - } +public: + static const ClassInfo s_info; - inline bool Arguments::trySetArgument(JSGlobalData& globalData, size_t argument, JSValue value) - { - if (!isArgument(argument)) - return false; - this->argument(argument).set(globalData, this, value); - return true; - } + static void visitChildren(JSCell*, SlotVisitor&); - inline JSValue Arguments::tryGetArgument(size_t argument) - { - if (!isArgument(argument)) - return JSValue(); - return this->argument(argument).get(); - } + void fillArgList(ExecState*, MarkedArgumentBuffer&); - inline bool Arguments::isDeletedArgument(size_t argument) + uint32_t length(ExecState* exec) const { - if (argument >= m_numArguments) - return false; - if (!m_slowArguments) - return false; - if (m_slowArguments[argument].status != SlowArgument::Deleted) - return false; - return true; + if (UNLIKELY(m_overrodeLength)) + return get(exec, exec->propertyNames().length).toUInt32(exec); + return m_numArguments; } - - inline bool Arguments::isArgument(size_t argument) - { - if (argument >= m_numArguments) - return false; - if (m_slowArguments && m_slowArguments[argument].status == SlowArgument::Deleted) - return false; - return true; + + void copyToArguments(ExecState*, CallFrame*, uint32_t length); + void tearOff(CallFrame*); + void tearOff(CallFrame*, InlineCallFrame*); + bool isTornOff() const { return m_registerArray; } + void didTearOffActivation(ExecState*, JSActivation*); + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); } - - inline WriteBarrierBase<Unknown>& Arguments::argument(size_t argument) - { - ASSERT(isArgument(argument)); - if (!m_slowArguments) - return m_registers[CallFrame::argumentOffset(argument)]; - - int index = m_slowArguments[argument].index; - if (!m_activation || m_slowArguments[argument].status != SlowArgument::Captured) - return m_registers[index]; - - return m_activation->registerAt(index); + +protected: + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags; + + void finishCreation(CallFrame*); + void finishCreation(CallFrame*, InlineCallFrame*); + +private: + static void destroy(JSCell*); + static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); + static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); + static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + static bool deleteProperty(JSCell*, ExecState*, PropertyName); + static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); + static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow); + void createStrictModeCallerIfNecessary(ExecState*); + void createStrictModeCalleeIfNecessary(ExecState*); + + bool isArgument(size_t); + bool trySetArgument(JSGlobalData&, size_t argument, JSValue); + JSValue tryGetArgument(size_t argument); + bool isDeletedArgument(size_t); + bool tryDeleteArgument(size_t); + WriteBarrierBase<Unknown>& argument(size_t); + void allocateSlowArguments(); + + void init(CallFrame*); + + WriteBarrier<JSActivation> m_activation; + + unsigned m_numArguments; + + // We make these full byte booleans to make them easy to test from the JIT, + // and because even if they were single-bit booleans we still wouldn't save + // any space. + bool m_overrodeLength; + bool m_overrodeCallee; + bool m_overrodeCaller; + bool m_isStrictMode; + + WriteBarrierBase<Unknown>* m_registers; + OwnArrayPtr<WriteBarrier<Unknown> > m_registerArray; + + OwnArrayPtr<SlowArgument> m_slowArguments; + + WriteBarrier<JSFunction> m_callee; +}; + +Arguments* asArguments(JSValue); + +inline Arguments* asArguments(JSValue value) +{ + ASSERT(asObject(value)->inherits(&Arguments::s_info)); + return static_cast<Arguments*>(asObject(value)); +} + +inline Arguments::Arguments(CallFrame* callFrame) + : JSDestructibleObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) +{ +} + +inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) + : JSDestructibleObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) +{ +} + +inline void Arguments::allocateSlowArguments() +{ + if (m_slowArguments) + return; + m_slowArguments = adoptArrayPtr(new SlowArgument[m_numArguments]); + for (size_t i = 0; i < m_numArguments; ++i) { + ASSERT(m_slowArguments[i].status == SlowArgument::Normal); + m_slowArguments[i].index = CallFrame::argumentOffset(i); } - - inline void Arguments::finishCreation(CallFrame* callFrame) - { - Base::finishCreation(callFrame->globalData()); - ASSERT(inherits(&s_info)); - - JSFunction* callee = jsCast<JSFunction*>(callFrame->callee()); - m_numArguments = callFrame->argumentCount(); - m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()); - m_callee.set(callFrame->globalData(), this, callee); - m_overrodeLength = false; - m_overrodeCallee = false; - m_overrodeCaller = false; - m_isStrictMode = callFrame->codeBlock()->isStrictMode(); - - SharedSymbolTable* symbolTable = callFrame->codeBlock()->symbolTable(); - const SlowArgument* slowArguments = symbolTable->slowArguments(); - if (slowArguments) { - allocateSlowArguments(); - size_t count = std::min<unsigned>(m_numArguments, symbolTable->parameterCount()); - for (size_t i = 0; i < count; ++i) - m_slowArguments[i] = slowArguments[i]; - } - - // The bytecode generator omits op_tear_off_activation in cases of no - // declared parameters, so we need to tear off immediately. - if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) - tearOff(callFrame); +} + +inline bool Arguments::tryDeleteArgument(size_t argument) +{ + if (!isArgument(argument)) + return false; + allocateSlowArguments(); + m_slowArguments[argument].status = SlowArgument::Deleted; + return true; +} + +inline bool Arguments::trySetArgument(JSGlobalData& globalData, size_t argument, JSValue value) +{ + if (!isArgument(argument)) + return false; + this->argument(argument).set(globalData, this, value); + return true; +} + +inline JSValue Arguments::tryGetArgument(size_t argument) +{ + if (!isArgument(argument)) + return JSValue(); + return this->argument(argument).get(); +} + +inline bool Arguments::isDeletedArgument(size_t argument) +{ + if (argument >= m_numArguments) + return false; + if (!m_slowArguments) + return false; + if (m_slowArguments[argument].status != SlowArgument::Deleted) + return false; + return true; +} + +inline bool Arguments::isArgument(size_t argument) +{ + if (argument >= m_numArguments) + return false; + if (m_slowArguments && m_slowArguments[argument].status == SlowArgument::Deleted) + return false; + return true; +} + +inline WriteBarrierBase<Unknown>& Arguments::argument(size_t argument) +{ + ASSERT(isArgument(argument)); + if (!m_slowArguments) + return m_registers[CallFrame::argumentOffset(argument)]; + + int index = m_slowArguments[argument].index; + if (!m_activation || m_slowArguments[argument].status != SlowArgument::Captured) + return m_registers[index]; + + return m_activation->registerAt(index); +} + +inline void Arguments::finishCreation(CallFrame* callFrame) +{ + Base::finishCreation(callFrame->globalData()); + ASSERT(inherits(&s_info)); + + JSFunction* callee = jsCast<JSFunction*>(callFrame->callee()); + m_numArguments = callFrame->argumentCount(); + m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()); + m_callee.set(callFrame->globalData(), this, callee); + m_overrodeLength = false; + m_overrodeCallee = false; + m_overrodeCaller = false; + m_isStrictMode = callFrame->codeBlock()->isStrictMode(); + + SharedSymbolTable* symbolTable = callFrame->codeBlock()->symbolTable(); + const SlowArgument* slowArguments = symbolTable->slowArguments(); + if (slowArguments) { + allocateSlowArguments(); + size_t count = std::min<unsigned>(m_numArguments, symbolTable->parameterCount()); + for (size_t i = 0; i < count; ++i) + m_slowArguments[i] = slowArguments[i]; } - inline void Arguments::finishCreation(CallFrame* callFrame, InlineCallFrame* inlineCallFrame) - { - Base::finishCreation(callFrame->globalData()); - ASSERT(inherits(&s_info)); - - JSFunction* callee = inlineCallFrame->callee.get(); - m_numArguments = inlineCallFrame->arguments.size() - 1; - m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()) + inlineCallFrame->stackOffset; - m_callee.set(callFrame->globalData(), this, callee); - m_overrodeLength = false; - m_overrodeCallee = false; - m_overrodeCaller = false; - m_isStrictMode = jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->isStrictMode(); - ASSERT(!jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->symbolTable(inlineCallFrame->isCall ? CodeForCall : CodeForConstruct)->slowArguments()); - - // The bytecode generator omits op_tear_off_activation in cases of no - // declared parameters, so we need to tear off immediately. - if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) - tearOff(callFrame, inlineCallFrame); - } + // The bytecode generator omits op_tear_off_activation in cases of no + // declared parameters, so we need to tear off immediately. + if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) + tearOff(callFrame); +} + +inline void Arguments::finishCreation(CallFrame* callFrame, InlineCallFrame* inlineCallFrame) +{ + Base::finishCreation(callFrame->globalData()); + ASSERT(inherits(&s_info)); + + JSFunction* callee = inlineCallFrame->callee.get(); + m_numArguments = inlineCallFrame->arguments.size() - 1; + m_registers = reinterpret_cast<WriteBarrierBase<Unknown>*>(callFrame->registers()) + inlineCallFrame->stackOffset; + m_callee.set(callFrame->globalData(), this, callee); + m_overrodeLength = false; + m_overrodeCallee = false; + m_overrodeCaller = false; + m_isStrictMode = jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->isStrictMode(); + ASSERT(!jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->symbolTable(inlineCallFrame->isCall ? CodeForCall : CodeForConstruct)->slowArguments()); + + // The bytecode generator omits op_tear_off_activation in cases of no + // declared parameters, so we need to tear off immediately. + if (m_isStrictMode || !callee->jsExecutable()->parameterCount()) + tearOff(callFrame, inlineCallFrame); +} } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp index 5c2cd7167..a3fce45f2 100644 --- a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp +++ b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp @@ -25,8 +25,8 @@ #include "ArrayConstructor.h" #include "ArrayPrototype.h" -#include "ButterflyInlineMethods.h" -#include "CopiedSpaceInlineMethods.h" +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" #include "Error.h" #include "ExceptionHelpers.h" #include "JSArray.h" @@ -77,15 +77,15 @@ bool ArrayConstructor::getOwnPropertyDescriptor(JSObject* object, ExecState* exe // ------------------------------ Functions --------------------------- -JSObject* constructArrayWithSizeQuirk(ExecState* exec, JSGlobalObject* globalObject, JSValue length) +JSObject* constructArrayWithSizeQuirk(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, JSValue length) { if (!length.isNumber()) - return constructArray(exec, globalObject, &length, 1); + return constructArray(exec, profile, globalObject, &length, 1); uint32_t n = length.toUInt32(exec); if (n != length.toNumber(exec)) return throwError(exec, createRangeError(exec, ASCIILiteral("Array size is not a small enough positive integer."))); - return constructEmptyArray(exec, globalObject, n); + return constructEmptyArray(exec, profile, globalObject, n); } static inline JSObject* constructArrayWithSizeQuirk(ExecState* exec, const ArgList& args) @@ -94,10 +94,10 @@ static inline JSObject* constructArrayWithSizeQuirk(ExecState* exec, const ArgLi // a single numeric argument denotes the array size (!) if (args.size() == 1) - return constructArrayWithSizeQuirk(exec, globalObject, args.at(0)); + return constructArrayWithSizeQuirk(exec, 0, globalObject, args.at(0)); // otherwise the array is constructed with the arguments in it - return constructArray(exec, globalObject, args); + return constructArray(exec, 0, globalObject, args); } static EncodedJSValue JSC_HOST_CALL constructWithArrayConstructor(ExecState* exec) diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.h b/Source/JavaScriptCore/runtime/ArrayConstructor.h index dcbf0a1b3..96860b0fc 100644 --- a/Source/JavaScriptCore/runtime/ArrayConstructor.h +++ b/Source/JavaScriptCore/runtime/ArrayConstructor.h @@ -25,42 +25,42 @@ namespace JSC { - class ArrayPrototype; - class JSArray; +class ArrayPrototype; +class JSArray; - class ArrayConstructor : public InternalFunction { - public: - typedef InternalFunction Base; +class ArrayConstructor : public InternalFunction { +public: + typedef InternalFunction Base; - static ArrayConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, ArrayPrototype* arrayPrototype) - { - ArrayConstructor* constructor = new (NotNull, allocateCell<ArrayConstructor>(*exec->heap())) ArrayConstructor(globalObject, structure); - constructor->finishCreation(exec, arrayPrototype); - return constructor; - } + static ArrayConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, ArrayPrototype* arrayPrototype) + { + ArrayConstructor* constructor = new (NotNull, allocateCell<ArrayConstructor>(*exec->heap())) ArrayConstructor(globalObject, structure); + constructor->finishCreation(exec, arrayPrototype); + return constructor; + } - static const ClassInfo s_info; + static const ClassInfo s_info; - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) - { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); - } + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } - protected: - void finishCreation(ExecState*, ArrayPrototype*); - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InternalFunction::StructureFlags; +protected: + void finishCreation(ExecState*, ArrayPrototype*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InternalFunction::StructureFlags; - private: - ArrayConstructor(JSGlobalObject*, Structure*); - static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); +private: + ArrayConstructor(JSGlobalObject*, Structure*); + static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); - static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); - static ConstructType getConstructData(JSCell*, ConstructData&); - static CallType getCallData(JSCell*, CallData&); - }; + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; - JSObject* constructArrayWithSizeQuirk(ExecState*, JSGlobalObject*, JSValue); +JSObject* constructArrayWithSizeQuirk(ExecState*, ArrayAllocationProfile*, JSGlobalObject*, JSValue); } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp index 6975dc778..e6d37fb02 100644 --- a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -24,10 +24,10 @@ #include "config.h" #include "ArrayPrototype.h" -#include "ButterflyInlineMethods.h" +#include "ButterflyInlines.h" #include "CachedCall.h" #include "CodeBlock.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "Interpreter.h" #include "JIT.h" #include "JSStringBuilder.h" @@ -116,15 +116,14 @@ const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, 0, ExecStat ArrayPrototype* ArrayPrototype::create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) { - Butterfly* butterfly = createArrayButterfly(exec->globalData(), 0); - ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure, butterfly); + ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure); prototype->finishCreation(globalObject); return prototype; } // ECMA 15.4.4 -ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure, Butterfly* butterfly) - : JSArray(globalObject->globalData(), structure, butterfly) +ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure) + : JSArray(globalObject->globalData(), structure, 0) { } @@ -456,7 +455,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec) EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); - JSArray* arr = constructEmptyArray(exec); + JSArray* arr = constructEmptyArray(exec, 0); unsigned n = 0; JSValue curArg = thisValue.toObject(exec); if (exec->hadException()) @@ -618,7 +617,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec) return JSValue::encode(jsUndefined()); // We return a new array - JSArray* resObj = constructEmptyArray(exec); + JSArray* resObj = constructEmptyArray(exec, 0); JSValue result = resObj; unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); @@ -733,7 +732,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) return JSValue::encode(jsUndefined()); if (!exec->argumentCount()) - return JSValue::encode(constructEmptyArray(exec)); + return JSValue::encode(constructEmptyArray(exec, 0)); unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); @@ -748,7 +747,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) deleteCount = static_cast<unsigned>(deleteDouble); } - JSArray* resObj = JSArray::tryCreateUninitialized(exec->globalData(), exec->lexicalGlobalObject()->arrayStructure(), deleteCount); + JSArray* resObj = JSArray::tryCreateUninitialized(exec->globalData(), exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount); if (!resObj) return JSValue::encode(throwOutOfMemoryError(exec)); @@ -820,7 +819,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec) return throwVMTypeError(exec); JSValue applyThis = exec->argument(1); - JSArray* resultArray = constructEmptyArray(exec); + JSArray* resultArray = constructEmptyArray(exec, 0); unsigned filterIndex = 0; unsigned k = 0; @@ -880,7 +879,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec) JSValue applyThis = exec->argument(1); - JSArray* resultArray = constructEmptyArray(exec, length); + JSArray* resultArray = constructEmptyArray(exec, 0, length); unsigned k = 0; if (callType == CallTypeJS && isJSArray(thisObj)) { JSFunction* f = jsCast<JSFunction*>(function); diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.h b/Source/JavaScriptCore/runtime/ArrayPrototype.h index b33021121..c23bcdec1 100644 --- a/Source/JavaScriptCore/runtime/ArrayPrototype.h +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.h @@ -26,28 +26,28 @@ namespace JSC { - class ArrayPrototype : public JSArray { - private: - ArrayPrototype(JSGlobalObject*, Structure*, Butterfly*); +class ArrayPrototype : public JSArray { +private: + ArrayPrototype(JSGlobalObject*, Structure*); - public: - typedef JSArray Base; +public: + typedef JSArray Base; - static ArrayPrototype* create(ExecState*, JSGlobalObject*, Structure*); + static ArrayPrototype* create(ExecState*, JSGlobalObject*, Structure*); - static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); - static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); + static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); - static const ClassInfo s_info; + static const ClassInfo s_info; - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) - { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage); - } + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayClass); + } - protected: - void finishCreation(JSGlobalObject*); - }; +protected: + void finishCreation(JSGlobalObject*); +}; } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h b/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h index 610b8d16c..178bf3fe9 100644 --- a/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h +++ b/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h @@ -31,25 +31,25 @@ namespace JSC { - class BatchedTransitionOptimizer { - WTF_MAKE_NONCOPYABLE(BatchedTransitionOptimizer); - public: - BatchedTransitionOptimizer(JSGlobalData& globalData, JSObject* object) - : m_globalData(&globalData) - , m_object(object) - { - } - - ~BatchedTransitionOptimizer() - { - if (m_object->structure()->isDictionary()) - m_object->flattenDictionaryObject(*m_globalData); - } - - private: - JSGlobalData* m_globalData; - JSObject* m_object; - }; +class BatchedTransitionOptimizer { + WTF_MAKE_NONCOPYABLE(BatchedTransitionOptimizer); +public: + BatchedTransitionOptimizer(JSGlobalData& globalData, JSObject* object) + : m_globalData(&globalData) + , m_object(object) + { + } + + ~BatchedTransitionOptimizer() + { + if (m_object->structure()->isDictionary()) + m_object->flattenDictionaryObject(*m_globalData); + } + +private: + JSGlobalData* m_globalData; + JSObject* m_object; +}; } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanConstructor.h b/Source/JavaScriptCore/runtime/BooleanConstructor.h index 2b6bafa2f..f395374ae 100644 --- a/Source/JavaScriptCore/runtime/BooleanConstructor.h +++ b/Source/JavaScriptCore/runtime/BooleanConstructor.h @@ -25,37 +25,37 @@ namespace JSC { - class BooleanPrototype; +class BooleanPrototype; - class BooleanConstructor : public InternalFunction { - public: - typedef InternalFunction Base; +class BooleanConstructor : public InternalFunction { +public: + typedef InternalFunction Base; - static BooleanConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, BooleanPrototype* booleanPrototype) - { - BooleanConstructor* constructor = new (NotNull, allocateCell<BooleanConstructor>(*exec->heap())) BooleanConstructor(globalObject, structure); - constructor->finishCreation(exec, booleanPrototype); - return constructor; - } + static BooleanConstructor* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, BooleanPrototype* booleanPrototype) + { + BooleanConstructor* constructor = new (NotNull, allocateCell<BooleanConstructor>(*exec->heap())) BooleanConstructor(globalObject, structure); + constructor->finishCreation(exec, booleanPrototype); + return constructor; + } - static const ClassInfo s_info; + static const ClassInfo s_info; - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) - { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); - } + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } - protected: - void finishCreation(ExecState*, BooleanPrototype*); +protected: + void finishCreation(ExecState*, BooleanPrototype*); - private: - BooleanConstructor(JSGlobalObject*, Structure*); - static ConstructType getConstructData(JSCell*, ConstructData&); - static CallType getCallData(JSCell*, CallData&); - }; +private: + BooleanConstructor(JSGlobalObject*, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; - JSObject* constructBooleanFromImmediateBoolean(ExecState*, JSGlobalObject*, JSValue); - JSObject* constructBoolean(ExecState*, const ArgList&); +JSObject* constructBooleanFromImmediateBoolean(ExecState*, JSGlobalObject*, JSValue); +JSObject* constructBoolean(ExecState*, const ArgList&); } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanObject.h b/Source/JavaScriptCore/runtime/BooleanObject.h index bd0f66944..b6fcccdcf 100644 --- a/Source/JavaScriptCore/runtime/BooleanObject.h +++ b/Source/JavaScriptCore/runtime/BooleanObject.h @@ -25,36 +25,36 @@ namespace JSC { - class BooleanObject : public JSWrapperObject { - protected: - JS_EXPORT_PRIVATE BooleanObject(JSGlobalData&, Structure*); - JS_EXPORT_PRIVATE void finishCreation(JSGlobalData&); - - public: - typedef JSWrapperObject Base; - - static BooleanObject* create(JSGlobalData& globalData, Structure* structure) - { - BooleanObject* boolean = new (NotNull, allocateCell<BooleanObject>(globalData.heap)) BooleanObject(globalData, structure); - boolean->finishCreation(globalData); - return boolean; - } - - static JS_EXPORTDATA const ClassInfo s_info; - - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) - { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); - } - }; +class BooleanObject : public JSWrapperObject { +protected: + JS_EXPORT_PRIVATE BooleanObject(JSGlobalData&, Structure*); + JS_EXPORT_PRIVATE void finishCreation(JSGlobalData&); - BooleanObject* asBooleanObject(JSValue); +public: + typedef JSWrapperObject Base; - inline BooleanObject* asBooleanObject(JSValue value) + static BooleanObject* create(JSGlobalData& globalData, Structure* structure) + { + BooleanObject* boolean = new (NotNull, allocateCell<BooleanObject>(globalData.heap)) BooleanObject(globalData, structure); + boolean->finishCreation(globalData); + return boolean; + } + + static JS_EXPORTDATA const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) { - ASSERT(asObject(value)->inherits(&BooleanObject::s_info)); - return static_cast<BooleanObject*>(asObject(value)); + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); } +}; + +BooleanObject* asBooleanObject(JSValue); + +inline BooleanObject* asBooleanObject(JSValue value) +{ + ASSERT(asObject(value)->inherits(&BooleanObject::s_info)); + return static_cast<BooleanObject*>(asObject(value)); +} } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/BooleanPrototype.h b/Source/JavaScriptCore/runtime/BooleanPrototype.h index 3767f76ed..05790a755 100644 --- a/Source/JavaScriptCore/runtime/BooleanPrototype.h +++ b/Source/JavaScriptCore/runtime/BooleanPrototype.h @@ -25,34 +25,34 @@ namespace JSC { - class BooleanPrototype : public BooleanObject { - public: - typedef BooleanObject Base; - - static BooleanPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) - { - BooleanPrototype* prototype = new (NotNull, allocateCell<BooleanPrototype>(*exec->heap())) BooleanPrototype(exec, structure); - prototype->finishCreation(exec, globalObject); - return prototype; - } +class BooleanPrototype : public BooleanObject { +public: + typedef BooleanObject Base; + + static BooleanPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) + { + BooleanPrototype* prototype = new (NotNull, allocateCell<BooleanPrototype>(*exec->heap())) BooleanPrototype(exec, structure); + prototype->finishCreation(exec, globalObject); + return prototype; + } - static const ClassInfo s_info; + static const ClassInfo s_info; - static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) - { - return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); - } + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } - protected: - void finishCreation(ExecState*, JSGlobalObject*); - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | BooleanObject::StructureFlags; +protected: + void finishCreation(ExecState*, JSGlobalObject*); + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | BooleanObject::StructureFlags; - private: - BooleanPrototype(ExecState*, Structure*); - static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); +private: + BooleanPrototype(ExecState*, Structure*); + static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); - static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); - }; + static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); +}; } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/Butterfly.h b/Source/JavaScriptCore/runtime/Butterfly.h index cb93aea8a..4b8d53f7e 100644 --- a/Source/JavaScriptCore/runtime/Butterfly.h +++ b/Source/JavaScriptCore/runtime/Butterfly.h @@ -88,12 +88,18 @@ public: template<typename T> T* indexingPayload() { return reinterpret_cast<T*>(this); } ArrayStorage* arrayStorage() { return indexingPayload<ArrayStorage>(); } + WriteBarrier<Unknown>* contiguousInt32() { return indexingPayload<WriteBarrier<Unknown> >(); } + double* contiguousDouble() { return indexingPayload<double>(); } WriteBarrier<Unknown>* contiguous() { return indexingPayload<WriteBarrier<Unknown> >(); } static Butterfly* fromContiguous(WriteBarrier<Unknown>* contiguous) { return reinterpret_cast<Butterfly*>(contiguous); } + static Butterfly* fromContiguous(double* contiguous) + { + return reinterpret_cast<Butterfly*>(contiguous); + } static ptrdiff_t offsetOfPropertyStorage() { return -static_cast<ptrdiff_t>(sizeof(IndexingHeader)); } static int indexOfPropertyStorage() diff --git a/Source/JavaScriptCore/runtime/ButterflyInlineMethods.h b/Source/JavaScriptCore/runtime/ButterflyInlines.h index 86a836bef..9167497a4 100644 --- a/Source/JavaScriptCore/runtime/ButterflyInlineMethods.h +++ b/Source/JavaScriptCore/runtime/ButterflyInlines.h @@ -23,12 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ButterflyInlineMethods_h -#define ButterflyInlineMethods_h +#ifndef ButterflyInlines_h +#define ButterflyInlines_h #include "ArrayStorage.h" #include "Butterfly.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "CopyVisitor.h" #include "JSGlobalData.h" #include "Structure.h" @@ -61,8 +61,9 @@ inline Butterfly* Butterfly::create(JSGlobalData& globalData, Structure* structu inline Butterfly* Butterfly::createUninitializedDuringCollection(CopyVisitor& visitor, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes) { + size_t size = totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); Butterfly* result = fromBase( - visitor.allocateNewSpace(totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes)), + visitor.allocateNewSpace(size), preCapacity, propertyCapacity); return result; } @@ -175,5 +176,5 @@ inline Butterfly* Butterfly::shift(Structure* structure, size_t numberOfSlots) } // namespace JSC -#endif // ButterflyInlineMethods_h +#endif // ButterflyInlines_h diff --git a/Source/JavaScriptCore/runtime/CallData.h b/Source/JavaScriptCore/runtime/CallData.h index 77478304c..7df36c72a 100644 --- a/Source/JavaScriptCore/runtime/CallData.h +++ b/Source/JavaScriptCore/runtime/CallData.h @@ -33,31 +33,31 @@ namespace JSC { - class ArgList; - class ExecState; - class FunctionExecutable; - class JSObject; - class JSScope; +class ArgList; +class ExecState; +class FunctionExecutable; +class JSObject; +class JSScope; - enum CallType { - CallTypeNone, - CallTypeHost, - CallTypeJS - }; +enum CallType { + CallTypeNone, + CallTypeHost, + CallTypeJS +}; - typedef EncodedJSValue (JSC_HOST_CALL *NativeFunction)(ExecState*); +typedef EncodedJSValue (JSC_HOST_CALL *NativeFunction)(ExecState*); - union CallData { - struct { - NativeFunction function; - } native; - struct { - FunctionExecutable* functionExecutable; - JSScope* scope; - } js; - }; +union CallData { + struct { + NativeFunction function; + } native; + struct { + FunctionExecutable* functionExecutable; + JSScope* scope; + } js; +}; - JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&); +JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&); } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ClassInfo.h b/Source/JavaScriptCore/runtime/ClassInfo.h index c918621fe..af1d71e13 100644 --- a/Source/JavaScriptCore/runtime/ClassInfo.h +++ b/Source/JavaScriptCore/runtime/ClassInfo.h @@ -29,90 +29,90 @@ namespace JSC { - class HashEntry; - struct HashTable; +class HashEntry; +struct HashTable; - struct MethodTable { - typedef void (*DestroyFunctionPtr)(JSCell*); - DestroyFunctionPtr destroy; +struct MethodTable { + typedef void (*DestroyFunctionPtr)(JSCell*); + DestroyFunctionPtr destroy; - typedef void (*VisitChildrenFunctionPtr)(JSCell*, SlotVisitor&); - VisitChildrenFunctionPtr visitChildren; + typedef void (*VisitChildrenFunctionPtr)(JSCell*, SlotVisitor&); + VisitChildrenFunctionPtr visitChildren; - typedef void (*CopyBackingStoreFunctionPtr)(JSCell*, CopyVisitor&); - CopyBackingStoreFunctionPtr copyBackingStore; + typedef void (*CopyBackingStoreFunctionPtr)(JSCell*, CopyVisitor&); + CopyBackingStoreFunctionPtr copyBackingStore; - typedef CallType (*GetCallDataFunctionPtr)(JSCell*, CallData&); - GetCallDataFunctionPtr getCallData; + typedef CallType (*GetCallDataFunctionPtr)(JSCell*, CallData&); + GetCallDataFunctionPtr getCallData; - typedef ConstructType (*GetConstructDataFunctionPtr)(JSCell*, ConstructData&); - GetConstructDataFunctionPtr getConstructData; + typedef ConstructType (*GetConstructDataFunctionPtr)(JSCell*, ConstructData&); + GetConstructDataFunctionPtr getConstructData; - typedef void (*PutFunctionPtr)(JSCell*, ExecState*, PropertyName propertyName, JSValue, PutPropertySlot&); - PutFunctionPtr put; + typedef void (*PutFunctionPtr)(JSCell*, ExecState*, PropertyName propertyName, JSValue, PutPropertySlot&); + PutFunctionPtr put; - typedef void (*PutByIndexFunctionPtr)(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); - PutByIndexFunctionPtr putByIndex; + typedef void (*PutByIndexFunctionPtr)(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + PutByIndexFunctionPtr putByIndex; - typedef bool (*DeletePropertyFunctionPtr)(JSCell*, ExecState*, PropertyName); - DeletePropertyFunctionPtr deleteProperty; + typedef bool (*DeletePropertyFunctionPtr)(JSCell*, ExecState*, PropertyName); + DeletePropertyFunctionPtr deleteProperty; - typedef bool (*DeletePropertyByIndexFunctionPtr)(JSCell*, ExecState*, unsigned); - DeletePropertyByIndexFunctionPtr deletePropertyByIndex; + typedef bool (*DeletePropertyByIndexFunctionPtr)(JSCell*, ExecState*, unsigned); + DeletePropertyByIndexFunctionPtr deletePropertyByIndex; - typedef bool (*GetOwnPropertySlotFunctionPtr)(JSCell*, ExecState*, PropertyName, PropertySlot&); - GetOwnPropertySlotFunctionPtr getOwnPropertySlot; + typedef bool (*GetOwnPropertySlotFunctionPtr)(JSCell*, ExecState*, PropertyName, PropertySlot&); + GetOwnPropertySlotFunctionPtr getOwnPropertySlot; - typedef bool (*GetOwnPropertySlotByIndexFunctionPtr)(JSCell*, ExecState*, unsigned, PropertySlot&); - GetOwnPropertySlotByIndexFunctionPtr getOwnPropertySlotByIndex; + typedef bool (*GetOwnPropertySlotByIndexFunctionPtr)(JSCell*, ExecState*, unsigned, PropertySlot&); + GetOwnPropertySlotByIndexFunctionPtr getOwnPropertySlotByIndex; - typedef JSObject* (*ToThisObjectFunctionPtr)(JSCell*, ExecState*); - ToThisObjectFunctionPtr toThisObject; + typedef JSObject* (*ToThisObjectFunctionPtr)(JSCell*, ExecState*); + ToThisObjectFunctionPtr toThisObject; - typedef JSValue (*DefaultValueFunctionPtr)(const JSObject*, ExecState*, PreferredPrimitiveType); - DefaultValueFunctionPtr defaultValue; + typedef JSValue (*DefaultValueFunctionPtr)(const JSObject*, ExecState*, PreferredPrimitiveType); + DefaultValueFunctionPtr defaultValue; - typedef void (*GetOwnPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); - GetOwnPropertyNamesFunctionPtr getOwnPropertyNames; + typedef void (*GetOwnPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + GetOwnPropertyNamesFunctionPtr getOwnPropertyNames; - typedef void (*GetOwnNonIndexPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); - GetOwnNonIndexPropertyNamesFunctionPtr getOwnNonIndexPropertyNames; + typedef void (*GetOwnNonIndexPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + GetOwnNonIndexPropertyNamesFunctionPtr getOwnNonIndexPropertyNames; - typedef void (*GetPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); - GetPropertyNamesFunctionPtr getPropertyNames; + typedef void (*GetPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + GetPropertyNamesFunctionPtr getPropertyNames; - typedef String (*ClassNameFunctionPtr)(const JSObject*); - ClassNameFunctionPtr className; + typedef String (*ClassNameFunctionPtr)(const JSObject*); + ClassNameFunctionPtr className; - typedef bool (*CustomHasInstanceFunctionPtr)(JSObject*, ExecState*, JSValue); - CustomHasInstanceFunctionPtr customHasInstance; + typedef bool (*CustomHasInstanceFunctionPtr)(JSObject*, ExecState*, JSValue); + CustomHasInstanceFunctionPtr customHasInstance; - typedef void (*PutWithAttributesFunctionPtr)(JSObject*, ExecState*, PropertyName propertyName, JSValue, unsigned attributes); - PutWithAttributesFunctionPtr putDirectVirtual; + typedef void (*PutWithAttributesFunctionPtr)(JSObject*, ExecState*, PropertyName propertyName, JSValue, unsigned attributes); + PutWithAttributesFunctionPtr putDirectVirtual; - typedef bool (*DefineOwnPropertyFunctionPtr)(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool); - DefineOwnPropertyFunctionPtr defineOwnProperty; + typedef bool (*DefineOwnPropertyFunctionPtr)(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool); + DefineOwnPropertyFunctionPtr defineOwnProperty; - typedef bool (*GetOwnPropertyDescriptorFunctionPtr)(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); - GetOwnPropertyDescriptorFunctionPtr getOwnPropertyDescriptor; - }; + typedef bool (*GetOwnPropertyDescriptorFunctionPtr)(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); + GetOwnPropertyDescriptorFunctionPtr getOwnPropertyDescriptor; +}; #define CREATE_MEMBER_CHECKER(member) \ -template <typename T> \ -struct MemberCheck##member { \ - struct Fallback { \ - void member(...); \ - }; \ - struct Derived : T, Fallback { }; \ - template <typename U, U> struct Check; \ - typedef char Yes[2]; \ - typedef char No[1]; \ - template <typename U> \ - static No &func(Check<void (Fallback::*)(...), &U::member>*); \ - template <typename U> \ - static Yes &func(...); \ - enum { has = sizeof(func<Derived>(0)) == sizeof(Yes) }; \ -} + template <typename T> \ + struct MemberCheck##member { \ + struct Fallback { \ + void member(...); \ + }; \ + struct Derived : T, Fallback { }; \ + template <typename U, U> struct Check; \ + typedef char Yes[2]; \ + typedef char No[1]; \ + template <typename U> \ + static No &func(Check<void (Fallback::*)(...), &U::member>*); \ + template <typename U> \ + static Yes &func(...); \ + enum { has = sizeof(func<Derived>(0)) == sizeof(Yes) }; \ + } #define HAS_MEMBER_NAMED(klass, name) (MemberCheck##name<klass>::has) @@ -141,54 +141,54 @@ struct MemberCheck##member { \ }, \ ClassName::TypedArrayStorageType - struct ClassInfo { - /** - * A string denoting the class name. Example: "Window". - */ - const char* className; - - /** - * Pointer to the class information of the base class. - * 0L if there is none. - */ - const ClassInfo* parentClass; - /** - * Static hash-table of properties. - * For classes that can be used from multiple threads, it is accessed via a getter function that would typically return a pointer to thread-specific value. - */ - const HashTable* propHashTable(ExecState* exec) const - { - if (classPropHashTableGetterFunction) - return classPropHashTableGetterFunction(exec); - return staticPropHashTable; - } +struct ClassInfo { + /** + * A string denoting the class name. Example: "Window". + */ + const char* className; + + /** + * Pointer to the class information of the base class. + * 0L if there is none. + */ + const ClassInfo* parentClass; + /** + * Static hash-table of properties. + * For classes that can be used from multiple threads, it is accessed via a getter function that would typically return a pointer to thread-specific value. + */ + const HashTable* propHashTable(ExecState* exec) const + { + if (classPropHashTableGetterFunction) + return classPropHashTableGetterFunction(exec); + return staticPropHashTable; + } - bool isSubClassOf(const ClassInfo* other) const - { - for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { - if (ci == other) - return true; - } - return false; + bool isSubClassOf(const ClassInfo* other) const + { + for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { + if (ci == other) + return true; } - - bool hasStaticProperties() const - { - for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { - if (ci->staticPropHashTable || ci->classPropHashTableGetterFunction) - return true; - } - return false; + return false; + } + + bool hasStaticProperties() const + { + for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { + if (ci->staticPropHashTable || ci->classPropHashTableGetterFunction) + return true; } + return false; + } - const HashTable* staticPropHashTable; - typedef const HashTable* (*ClassPropHashTableGetterFunction)(ExecState*); - const ClassPropHashTableGetterFunction classPropHashTableGetterFunction; + const HashTable* staticPropHashTable; + typedef const HashTable* (*ClassPropHashTableGetterFunction)(ExecState*); + const ClassPropHashTableGetterFunction classPropHashTableGetterFunction; - MethodTable methodTable; + MethodTable methodTable; - TypedArrayType typedArrayStorageType; - }; + TypedArrayType typedArrayStorageType; +}; } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/CodeCache.cpp b/Source/JavaScriptCore/runtime/CodeCache.cpp index 4de760e49..068919528 100644 --- a/Source/JavaScriptCore/runtime/CodeCache.cpp +++ b/Source/JavaScriptCore/runtime/CodeCache.cpp @@ -36,7 +36,6 @@ namespace JSC { CodeCache::CodeCache() - : m_randomGenerator(static_cast<uint32_t>(randomNumber() * UINT32_MAX)) { } @@ -67,9 +66,9 @@ UnlinkedCodeBlockType* CodeCache::getCodeBlock(JSGlobalData& globalData, Executa CodeBlockKey key = makeCodeBlockKey(source, CacheTypes<UnlinkedCodeBlockType>::codeType, strictness); bool storeInCache = false; if (debuggerMode == DebuggerOff && profilerMode == ProfilerOff) { - CodeBlockIndicesMap::iterator result = m_cachedCodeBlockIndices.find(key); - if (result != m_cachedCodeBlockIndices.end()) { - UnlinkedCodeBlockType* unlinkedCode = jsCast<UnlinkedCodeBlockType*>(m_cachedCodeBlocks[result->value].second.get()); + const Strong<UnlinkedCodeBlock>* result = m_cachedCodeBlocks.find(key); + if (result) { + UnlinkedCodeBlockType* unlinkedCode = jsCast<UnlinkedCodeBlockType*>(result->get()); unsigned firstLine = source.firstLine() + unlinkedCode->firstLine(); executable->recordParse(unlinkedCode->codeFeatures(), unlinkedCode->hasCapturedVariables(), firstLine, firstLine + unlinkedCode->lineCount()); return unlinkedCode; @@ -91,14 +90,8 @@ UnlinkedCodeBlockType* CodeCache::getCodeBlock(JSGlobalData& globalData, Executa if (error.m_type != ParserError::ErrorNone) return 0; - if (storeInCache) { - size_t index = m_randomGenerator.getUint32() % kMaxCodeBlockEntries; - if (m_cachedCodeBlocks[index].second) - m_cachedCodeBlockIndices.remove(m_cachedCodeBlocks[index].first); - m_cachedCodeBlockIndices.set(key, index); - m_cachedCodeBlocks[index].second.set(globalData, unlinkedCode); - m_cachedCodeBlocks[index].first = key; - } + if (storeInCache) + m_cachedCodeBlocks.add(key, Strong<UnlinkedCodeBlock>(globalData, unlinkedCode)); return unlinkedCode; } @@ -133,6 +126,7 @@ UnlinkedFunctionCodeBlock* CodeCache::generateFunctionCodeBlock(JSGlobalData& gl body->destroyData(); if (error.m_type != ParserError::ErrorNone) return 0; + m_cachedFunctionCode.add(result, Strong<UnlinkedFunctionCodeBlock>(globalData, result)); return result; } @@ -149,9 +143,9 @@ CodeCache::GlobalFunctionKey CodeCache::makeGlobalFunctionKey(const SourceCode& UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(JSGlobalData& globalData, const Identifier& name, const SourceCode& source, ParserError& error) { GlobalFunctionKey key = makeGlobalFunctionKey(source, name.string()); - GlobalFunctionIndicesMap::iterator result = m_cachedGlobalFunctionIndices.find(key); - if (result != m_cachedGlobalFunctionIndices.end()) - return m_cachedGlobalFunctions[result->value].second.get(); + const Strong<UnlinkedFunctionExecutable>* result = m_cachedGlobalFunctions.find(key); + if (result) + return result->get(); RefPtr<ProgramNode> program = parse<ProgramNode>(&globalData, source, 0, Identifier(), JSParseNormal, JSParseProgramCode, error); if (!program) { @@ -173,14 +167,13 @@ UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(JSGlo UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&globalData, source, body); functionExecutable->m_nameValue.set(globalData, functionExecutable, jsString(&globalData, name.string())); - size_t index = m_randomGenerator.getUint32() % kMaxGlobalFunctionEntries; - if (m_cachedGlobalFunctions[index].second) - m_cachedGlobalFunctionIndices.remove(m_cachedGlobalFunctions[index].first); - m_cachedGlobalFunctionIndices.set(key, index); - m_cachedGlobalFunctions[index].second.set(globalData, functionExecutable); - m_cachedGlobalFunctions[index].first = key; - + m_cachedGlobalFunctions.add(key, Strong<UnlinkedFunctionExecutable>(globalData, functionExecutable)); return functionExecutable; } +void CodeCache::usedFunctionCode(JSGlobalData& globalData, UnlinkedFunctionCodeBlock* codeBlock) +{ + m_cachedFunctionCode.add(codeBlock, Strong<UnlinkedFunctionCodeBlock>(globalData, codeBlock)); +} + } diff --git a/Source/JavaScriptCore/runtime/CodeCache.h b/Source/JavaScriptCore/runtime/CodeCache.h index 4d4617189..733de42d6 100644 --- a/Source/JavaScriptCore/runtime/CodeCache.h +++ b/Source/JavaScriptCore/runtime/CodeCache.h @@ -34,6 +34,7 @@ #include <wtf/FixedArray.h> #include <wtf/Forward.h> #include <wtf/PassOwnPtr.h> +#include <wtf/RandomNumber.h> #include <wtf/text/WTFString.h> namespace JSC { @@ -51,6 +52,41 @@ struct ParserError; class SourceCode; class SourceProvider; +template <typename KeyType, typename EntryType, int CacheSize> class CacheMap { + typedef typename HashMap<KeyType, unsigned>::iterator iterator; +public: + CacheMap() + : m_randomGenerator((static_cast<uint32_t>(randomNumber() * std::numeric_limits<uint32_t>::max()))) + { + } + const EntryType* find(const KeyType& key) + { + iterator result = m_map.find(key); + if (result == m_map.end()) + return 0; + return &m_data[result->value].second; + } + void add(const KeyType& key, const EntryType& value) + { + iterator result = m_map.find(key); + if (result != m_map.end()) { + m_data[result->value].second = value; + return; + } + size_t newIndex = m_randomGenerator.getUint32() % CacheSize; + if (m_data[newIndex].second) + m_map.remove(m_data[newIndex].first); + m_map.add(key, newIndex); + m_data[newIndex].first = key; + m_data[newIndex].second = value; + ASSERT(m_map.size() <= CacheSize); + } +private: + HashMap<KeyType, unsigned> m_map; + FixedArray<std::pair<KeyType, EntryType>, CacheSize> m_data; + WeakRandom m_randomGenerator; +}; + class CodeCache { public: static PassOwnPtr<CodeCache> create() { return adoptPtr(new CodeCache); } @@ -59,13 +95,12 @@ public: UnlinkedEvalCodeBlock* getEvalCodeBlock(JSGlobalData&, EvalExecutable*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&); UnlinkedFunctionCodeBlock* getFunctionCodeBlock(JSGlobalData&, UnlinkedFunctionExecutable*, const SourceCode&, CodeSpecializationKind, DebuggerMode, ProfilerMode, ParserError&); UnlinkedFunctionExecutable* getFunctionExecutableFromGlobalCode(JSGlobalData&, const Identifier&, const SourceCode&, ParserError&); + void usedFunctionCode(JSGlobalData&, UnlinkedFunctionCodeBlock*); ~CodeCache(); enum CodeType { EvalType, ProgramType, FunctionType }; typedef std::pair<String, unsigned> CodeBlockKey; - typedef HashMap<CodeBlockKey, unsigned> CodeBlockIndicesMap; typedef std::pair<String, String> GlobalFunctionKey; - typedef HashMap<GlobalFunctionKey, unsigned> GlobalFunctionIndicesMap; private: CodeCache(); @@ -74,18 +109,17 @@ private: template <class UnlinkedCodeBlockType, class ExecutableType> inline UnlinkedCodeBlockType* getCodeBlock(JSGlobalData&, ExecutableType*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&); CodeBlockKey makeCodeBlockKey(const SourceCode&, CodeType, JSParserStrictness); - CodeBlockIndicesMap m_cachedCodeBlockIndices; GlobalFunctionKey makeGlobalFunctionKey(const SourceCode&, const String&); - GlobalFunctionIndicesMap m_cachedGlobalFunctionIndices; enum { kMaxCodeBlockEntries = 1024, - kMaxGlobalFunctionEntries = 1024 + kMaxGlobalFunctionEntries = 1024, + kMaxFunctionCodeBlocks = 1024 }; - FixedArray<std::pair<CodeBlockKey, Strong<UnlinkedCodeBlock> >, kMaxCodeBlockEntries> m_cachedCodeBlocks; - FixedArray<std::pair<GlobalFunctionKey, Strong<UnlinkedFunctionExecutable> >, kMaxGlobalFunctionEntries> m_cachedGlobalFunctions; - WeakRandom m_randomGenerator; + CacheMap<CodeBlockKey, Strong<UnlinkedCodeBlock>, kMaxCodeBlockEntries> m_cachedCodeBlocks; + CacheMap<GlobalFunctionKey, Strong<UnlinkedFunctionExecutable>, kMaxGlobalFunctionEntries> m_cachedGlobalFunctions; + CacheMap<UnlinkedFunctionCodeBlock*, Strong<UnlinkedFunctionCodeBlock>, kMaxFunctionCodeBlocks> m_cachedFunctionCode; }; } diff --git a/Source/JavaScriptCore/runtime/Executable.cpp b/Source/JavaScriptCore/runtime/Executable.cpp index 20a2e2acb..49a0e256d 100644 --- a/Source/JavaScriptCore/runtime/Executable.cpp +++ b/Source/JavaScriptCore/runtime/Executable.cpp @@ -620,18 +620,17 @@ void FunctionExecutable::clearCodeIfNotCompiling() clearCode(); } -void FunctionExecutable::clearUnlinkedCodeIfNotCompiling() +void FunctionExecutable::clearUnlinkedCodeForRecompilationIfNotCompiling() { if (isCompiling()) return; - m_unlinkedExecutable->clearCode(); + m_unlinkedExecutable->clearCodeForRecompilation(); } void FunctionExecutable::clearCode() { m_codeBlockForCall.clear(); m_codeBlockForConstruct.clear(); - m_unlinkedExecutable->clearCode(); Base::clearCode(); } diff --git a/Source/JavaScriptCore/runtime/Executable.h b/Source/JavaScriptCore/runtime/Executable.h index 74b4add75..c1c044b0e 100644 --- a/Source/JavaScriptCore/runtime/Executable.h +++ b/Source/JavaScriptCore/runtime/Executable.h @@ -704,7 +704,7 @@ namespace JSC { SharedSymbolTable* symbolTable(CodeSpecializationKind kind) const { return m_unlinkedExecutable->symbolTable(kind); } void clearCodeIfNotCompiling(); - void clearUnlinkedCodeIfNotCompiling(); + void clearUnlinkedCodeForRecompilationIfNotCompiling(); static void visitChildren(JSCell*, SlotVisitor&); static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto) { @@ -752,6 +752,7 @@ namespace JSC { : Base(globalData, scope->globalObject()->functionStructure()) , m_executable(globalData, this, executable) , m_scope(globalData, this, scope) + , m_inheritorIDWatchpoint(InitializedBlind) // See comment in JSFunction.cpp concerning the reason for using InitializedBlind as opposed to InitializedWatching. { } diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp index a4b2202c1..8e4390b1b 100644 --- a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp +++ b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp @@ -186,7 +186,7 @@ EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState* exec) // Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order. size_t numBoundArgs = exec->argumentCount() > 1 ? exec->argumentCount() - 1 : 0; - JSArray* boundArgs = JSArray::tryCreateUninitialized(exec->globalData(), globalObject->arrayStructure(), numBoundArgs); + JSArray* boundArgs = JSArray::tryCreateUninitialized(exec->globalData(), globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), numBoundArgs); if (!boundArgs) return JSValue::encode(throwOutOfMemoryError(exec)); diff --git a/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h b/Source/JavaScriptCore/runtime/IndexingHeaderInlines.h index 22785ce24..cfad1c8c2 100644 --- a/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h +++ b/Source/JavaScriptCore/runtime/IndexingHeaderInlines.h @@ -23,8 +23,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IndexingHeaderInlineMethods_h -#define IndexingHeaderInlineMethods_h +#ifndef IndexingHeaderInlines_h +#define IndexingHeaderInlines_h #include "ArrayStorage.h" #include "IndexingHeader.h" @@ -43,6 +43,9 @@ inline size_t IndexingHeader::preCapacity(Structure* structure) inline size_t IndexingHeader::indexingPayloadSizeInBytes(Structure* structure) { switch (structure->indexingType()) { + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return vectorLength() * sizeof(EncodedJSValue); @@ -57,5 +60,5 @@ inline size_t IndexingHeader::indexingPayloadSizeInBytes(Structure* structure) } // namespace JSC -#endif // IndexingHeaderInlineMethods_h +#endif // IndexingHeaderInlines_h diff --git a/Source/JavaScriptCore/runtime/IndexingType.cpp b/Source/JavaScriptCore/runtime/IndexingType.cpp index 7261847a2..dc2733ad1 100644 --- a/Source/JavaScriptCore/runtime/IndexingType.cpp +++ b/Source/JavaScriptCore/runtime/IndexingType.cpp @@ -31,6 +31,46 @@ namespace JSC { +IndexingType leastUpperBoundOfIndexingTypes(IndexingType a, IndexingType b) +{ + // It doesn't make sense to LUB something that is an array with something that isn't. + ASSERT((a & IsArray) == (b & IsArray)); + + // Boy, this sure is easy right now. + return std::max(a, b); +} + +IndexingType leastUpperBoundOfIndexingTypeAndType(IndexingType indexingType, SpeculatedType type) +{ + if (!type) + return indexingType; + switch (indexingType) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + if (isInt32Speculation(type)) + return (indexingType & ~IndexingShapeMask) | Int32Shape; + if (isNumberSpeculation(type)) + return (indexingType & ~IndexingShapeMask) | DoubleShape; + return (indexingType & ~IndexingShapeMask) | ContiguousShape; + case ALL_DOUBLE_INDEXING_TYPES: + if (isNumberSpeculation(type)) + return indexingType; + return (indexingType & ~IndexingShapeMask) | ContiguousShape; + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return indexingType; + default: + CRASH(); + return 0; + } +} + +IndexingType leastUpperBoundOfIndexingTypeAndValue(IndexingType indexingType, JSValue value) +{ + return leastUpperBoundOfIndexingTypeAndType(indexingType, speculationFromValue(value)); +} + const char* indexingTypeToString(IndexingType indexingType) { static char result[128]; @@ -39,6 +79,12 @@ const char* indexingTypeToString(IndexingType indexingType) case NonArray: basicName = "NonArray"; break; + case NonArrayWithInt32: + basicName = "NonArrayWithInt32"; + break; + case NonArrayWithDouble: + basicName = "NonArrayWithDouble"; + break; case NonArrayWithContiguous: basicName = "NonArrayWithContiguous"; break; @@ -51,6 +97,15 @@ const char* indexingTypeToString(IndexingType indexingType) case ArrayClass: basicName = "ArrayClass"; break; + case ArrayWithUndecided: + basicName = "ArrayWithUndecided"; + break; + case ArrayWithInt32: + basicName = "ArrayWithInt32"; + break; + case ArrayWithDouble: + basicName = "ArrayWithDouble"; + break; case ArrayWithContiguous: basicName = "ArrayWithContiguous"; break; diff --git a/Source/JavaScriptCore/runtime/IndexingType.h b/Source/JavaScriptCore/runtime/IndexingType.h index 4bbe3cfa0..ab253be1e 100644 --- a/Source/JavaScriptCore/runtime/IndexingType.h +++ b/Source/JavaScriptCore/runtime/IndexingType.h @@ -26,6 +26,7 @@ #ifndef IndexingType_h #define IndexingType_h +#include "SpeculatedType.h" #include <wtf/StdLibExtras.h> namespace JSC { @@ -37,21 +38,32 @@ static const IndexingType IsArray = 1; // The shape of the indexed property storage. static const IndexingType IndexingShapeMask = 30; -static const IndexingType NoIndexingShape = 0; +static const IndexingType NoIndexingShape = 0; +static const IndexingType UndecidedShape = 2; // Only useful for arrays. +static const IndexingType Int32Shape = 20; +static const IndexingType DoubleShape = 22; static const IndexingType ContiguousShape = 26; static const IndexingType ArrayStorageShape = 28; static const IndexingType SlowPutArrayStorageShape = 30; +static const IndexingType IndexingShapeShift = 1; +static const IndexingType NumberOfIndexingShapes = 16; + // Additional flags for tracking the history of the type. These are usually // masked off unless you ask for them directly. static const IndexingType MayHaveIndexedAccessors = 32; // List of acceptable array types. static const IndexingType NonArray = 0; +static const IndexingType NonArrayWithInt32 = Int32Shape; +static const IndexingType NonArrayWithDouble = DoubleShape; static const IndexingType NonArrayWithContiguous = ContiguousShape; static const IndexingType NonArrayWithArrayStorage = ArrayStorageShape; static const IndexingType NonArrayWithSlowPutArrayStorage = SlowPutArrayStorageShape; static const IndexingType ArrayClass = IsArray; // I'd want to call this "Array" but this would lead to disastrous namespace pollution. +static const IndexingType ArrayWithUndecided = IsArray | UndecidedShape; +static const IndexingType ArrayWithInt32 = IsArray | Int32Shape; +static const IndexingType ArrayWithDouble = IsArray | DoubleShape; static const IndexingType ArrayWithContiguous = IsArray | ContiguousShape; static const IndexingType ArrayWithArrayStorage = IsArray | ArrayStorageShape; static const IndexingType ArrayWithSlowPutArrayStorage = IsArray | SlowPutArrayStorageShape; @@ -60,6 +72,17 @@ static const IndexingType ArrayWithSlowPutArrayStorage = IsArray | SlowPutArr NonArray: \ case ArrayClass +#define ALL_UNDECIDED_INDEXING_TYPES \ + ArrayWithUndecided + +#define ALL_INT32_INDEXING_TYPES \ + NonArrayWithInt32: \ + case ArrayWithInt32 + +#define ALL_DOUBLE_INDEXING_TYPES \ + NonArrayWithDouble: \ + case ArrayWithDouble + #define ALL_CONTIGUOUS_INDEXING_TYPES \ NonArrayWithContiguous: \ case ArrayWithContiguous @@ -83,6 +106,21 @@ static inline bool hasIndexingHeader(IndexingType type) return hasIndexedProperties(type); } +static inline bool hasUndecided(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == UndecidedShape; +} + +static inline bool hasInt32(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == Int32Shape; +} + +static inline bool hasDouble(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == DoubleShape; +} + static inline bool hasContiguous(IndexingType indexingType) { return (indexingType & IndexingShapeMask) == ContiguousShape; @@ -105,6 +143,12 @@ static inline bool shouldUseSlowPut(IndexingType indexingType) return (indexingType & IndexingShapeMask) == SlowPutArrayStorageShape; } +// Return an indexing type that can handle all of the elements of both indexing types. +IndexingType leastUpperBoundOfIndexingTypes(IndexingType, IndexingType); + +IndexingType leastUpperBoundOfIndexingTypeAndType(IndexingType, SpeculatedType); +IndexingType leastUpperBoundOfIndexingTypeAndValue(IndexingType, JSValue); + const char* indexingTypeToString(IndexingType); // Mask of all possible types. diff --git a/Source/JavaScriptCore/runtime/JSActivation.h b/Source/JavaScriptCore/runtime/JSActivation.h index fc6336463..b8f5621af 100644 --- a/Source/JavaScriptCore/runtime/JSActivation.h +++ b/Source/JavaScriptCore/runtime/JSActivation.h @@ -30,7 +30,7 @@ #define JSActivation_h #include "CodeBlock.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "JSVariableObject.h" #include "Nodes.h" #include "SymbolTable.h" diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp index d1ece1a36..4ba5cc2bd 100644 --- a/Source/JavaScriptCore/runtime/JSArray.cpp +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -24,14 +24,14 @@ #include "JSArray.h" #include "ArrayPrototype.h" -#include "ButterflyInlineMethods.h" -#include "CopiedSpace.h" -#include "CopiedSpaceInlineMethods.h" +#include "ButterflyInlines.h" #include "CachedCall.h" +#include "CopiedSpace.h" +#include "CopiedSpaceInlines.h" #include "Error.h" #include "Executable.h" #include "GetterSetter.h" -#include "IndexingHeaderInlineMethods.h" +#include "IndexingHeaderInlines.h" #include "PropertyNameArray.h" #include "Reject.h" #include <wtf/AVLTree.h> @@ -410,25 +410,33 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException exec, newLength, throwException, convertContiguousToArrayStorage(exec->globalData())); } - createInitialContiguous(exec->globalData(), newLength); + createInitialUndecided(exec->globalData(), newLength); return true; + case ArrayWithUndecided: + case ArrayWithInt32: + case ArrayWithDouble: case ArrayWithContiguous: if (newLength == m_butterfly->publicLength()) return true; if (newLength >= MAX_ARRAY_INDEX // This case ensures that we can do fast push. || (newLength >= MIN_SPARSE_ARRAY_INDEX - && !isDenseEnoughForVector(newLength, countElementsInContiguous(m_butterfly)))) { + && !isDenseEnoughForVector(newLength, countElements()))) { return setLengthWithArrayStorage( exec, newLength, throwException, - convertContiguousToArrayStorage(exec->globalData())); + ensureArrayStorage(exec->globalData())); } if (newLength > m_butterfly->publicLength()) { - ensureContiguousLength(exec->globalData(), newLength); + ensureLength(exec->globalData(), newLength); return true; } - for (unsigned i = m_butterfly->publicLength(); i-- > newLength;) - m_butterfly->contiguous()[i].clear(); + if (structure()->indexingType() == ArrayWithDouble) { + for (unsigned i = m_butterfly->publicLength(); i-- > newLength;) + m_butterfly->contiguousDouble()[i] = QNaN; + } else { + for (unsigned i = m_butterfly->publicLength(); i-- > newLength;) + m_butterfly->contiguous()[i].clear(); + } m_butterfly->setPublicLength(newLength); return true; @@ -448,6 +456,13 @@ JSValue JSArray::pop(ExecState* exec) case ArrayClass: return jsUndefined(); + case ArrayWithUndecided: + if (!m_butterfly->publicLength()) + return jsUndefined(); + // We have nothing but holes. So, drop down to the slow version. + break; + + case ArrayWithInt32: case ArrayWithContiguous: { unsigned length = m_butterfly->publicLength(); @@ -464,6 +479,22 @@ JSValue JSArray::pop(ExecState* exec) break; } + case ArrayWithDouble: { + unsigned length = m_butterfly->publicLength(); + + if (!length--) + return jsUndefined(); + + ASSERT(length < m_butterfly->vectorLength()); + double value = m_butterfly->contiguousDouble()[length]; + if (value == value) { + m_butterfly->contiguousDouble()[length] = QNaN; + m_butterfly->setPublicLength(length); + return JSValue(JSValue::EncodeAsDouble, value); + } + break; + } + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); @@ -518,10 +549,42 @@ void JSArray::push(ExecState* exec, JSValue value) { switch (structure()->indexingType()) { case ArrayClass: { - putByIndexBeyondVectorLengthWithArrayStorage(exec, 0, value, true, createInitialArrayStorage(exec->globalData())); - break; + createInitialUndecided(exec->globalData(), 0); + // Fall through. + } + + case ArrayWithUndecided: { + convertUndecidedForValue(exec->globalData(), value); + push(exec, value); + return; } + case ArrayWithInt32: { + if (!value.isInt32()) { + convertInt32ForValue(exec->globalData(), value); + push(exec, value); + return; + } + + unsigned length = m_butterfly->publicLength(); + ASSERT(length <= m_butterfly->vectorLength()); + if (length < m_butterfly->vectorLength()) { + m_butterfly->contiguousInt32()[length].setWithoutWriteBarrier(value); + m_butterfly->setPublicLength(length + 1); + return; + } + + if (length > MAX_ARRAY_INDEX) { + methodTable()->putByIndex(this, exec, length, value, true); + if (!exec->hadException()) + throwError(exec, createRangeError(exec, "Invalid array length")); + return; + } + + putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, length, value); + return; + } + case ArrayWithContiguous: { unsigned length = m_butterfly->publicLength(); ASSERT(length <= m_butterfly->vectorLength()); @@ -538,10 +601,42 @@ void JSArray::push(ExecState* exec, JSValue value) return; } - putByIndexBeyondVectorLengthContiguousWithoutAttributes(exec, length, value); + putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, length, value); return; } + case ArrayWithDouble: { + if (!value.isNumber()) { + convertDoubleToContiguous(exec->globalData()); + push(exec, value); + return; + } + double valueAsDouble = value.asNumber(); + if (valueAsDouble != valueAsDouble) { + convertDoubleToContiguous(exec->globalData()); + push(exec, value); + return; + } + + unsigned length = m_butterfly->publicLength(); + ASSERT(length <= m_butterfly->vectorLength()); + if (length < m_butterfly->vectorLength()) { + m_butterfly->contiguousDouble()[length] = valueAsDouble; + m_butterfly->setPublicLength(length + 1); + return; + } + + if (length > MAX_ARRAY_INDEX) { + methodTable()->putByIndex(this, exec, length, value, true); + if (!exec->hadException()) + throwError(exec, createRangeError(exec, "Invalid array length")); + return; + } + + putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, length, value); + break; + } + case ArrayWithSlowPutArrayStorage: { unsigned oldLength = length(); if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true)) { @@ -647,6 +742,11 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex case ArrayClass: return true; + case ArrayWithUndecided: + // Don't handle this because it's confusing and it shouldn't come up. + return false; + + case ArrayWithInt32: case ArrayWithContiguous: { unsigned oldLength = m_butterfly->publicLength(); ASSERT(count <= oldLength); @@ -654,7 +754,7 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex // We may have to walk the entire array to do the shift. We're willing to do // so only if it's not horribly slow. if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX) - return shiftCountWithArrayStorage(startIndex, count, convertContiguousToArrayStorage(exec->globalData())); + return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData())); unsigned end = oldLength - count; for (unsigned i = startIndex; i < end; ++i) { @@ -668,7 +768,7 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex // about holes (at least for now), but it can detect them quickly. So // we convert to array storage and then allow the array storage path to // figure it out. - return shiftCountWithArrayStorage(startIndex, count, convertContiguousToArrayStorage(exec->globalData())); + return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData())); } // No need for a barrier since we're just moving data around in the same vector. // This is in line with our standing assumption that we won't have a deletion @@ -682,6 +782,41 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex return true; } + case ArrayWithDouble: { + unsigned oldLength = m_butterfly->publicLength(); + ASSERT(count <= oldLength); + + // We may have to walk the entire array to do the shift. We're willing to do + // so only if it's not horribly slow. + if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX) + return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData())); + + unsigned end = oldLength - count; + for (unsigned i = startIndex; i < end; ++i) { + // Storing to a hole is fine since we're still having a good time. But reading + // from a hole is totally not fine, since we might have to read from the proto + // chain. + double v = m_butterfly->contiguousDouble()[i + count]; + if (UNLIKELY(v != v)) { + // The purpose of this path is to ensure that we don't make the same + // mistake in the future: shiftCountWithArrayStorage() can't do anything + // about holes (at least for now), but it can detect them quickly. So + // we convert to array storage and then allow the array storage path to + // figure it out. + return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData())); + } + // No need for a barrier since we're just moving data around in the same vector. + // This is in line with our standing assumption that we won't have a deletion + // barrier. + m_butterfly->contiguousDouble()[i] = v; + } + for (unsigned i = end; i < oldLength; ++i) + m_butterfly->contiguousDouble()[i] = QNaN; + + m_butterfly->setPublicLength(oldLength - count); + return true; + } + case ArrayWithArrayStorage: case ArrayWithSlowPutArrayStorage: return shiftCountWithArrayStorage(startIndex, count, arrayStorage()); @@ -740,23 +875,25 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd { switch (structure()->indexingType()) { case ArrayClass: + case ArrayWithUndecided: // We could handle this. But it shouldn't ever come up, so we won't. return false; - + + case ArrayWithInt32: case ArrayWithContiguous: { unsigned oldLength = m_butterfly->publicLength(); // We may have to walk the entire array to do the unshift. We're willing to do so // only if it's not horribly slow. if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) - return unshiftCountWithArrayStorage(exec, startIndex, count, convertContiguousToArrayStorage(exec->globalData())); + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData())); - ensureContiguousLength(exec->globalData(), oldLength + count); + ensureLength(exec->globalData(), oldLength + count); for (unsigned i = oldLength; i-- > startIndex;) { JSValue v = m_butterfly->contiguous()[i].get(); if (UNLIKELY(!v)) - return unshiftCountWithArrayStorage(exec, startIndex, count, convertContiguousToArrayStorage(exec->globalData())); + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData())); m_butterfly->contiguous()[i + count].setWithoutWriteBarrier(v); } @@ -768,6 +905,31 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd return true; } + case ArrayWithDouble: { + unsigned oldLength = m_butterfly->publicLength(); + + // We may have to walk the entire array to do the unshift. We're willing to do so + // only if it's not horribly slow. + if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData())); + + ensureLength(exec->globalData(), oldLength + count); + + for (unsigned i = oldLength; i-- > startIndex;) { + double v = m_butterfly->contiguousDouble()[i]; + if (UNLIKELY(v != v)) + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData())); + m_butterfly->contiguousDouble()[i + count] = v; + } + + // NOTE: we're leaving being garbage in the part of the array that we shifted out + // of. This is fine because the caller is required to store over that area, and + // in contiguous mode storing into a hole is guaranteed to behave exactly the same + // as storing over an existing element. + + return true; + } + case ArrayWithArrayStorage: case ArrayWithSlowPutArrayStorage: return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage()); @@ -778,6 +940,20 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd } } +static int compareNumbersForQSortWithInt32(const void* a, const void* b) +{ + int32_t ia = static_cast<const JSValue*>(a)->asInt32(); + int32_t ib = static_cast<const JSValue*>(b)->asInt32(); + return ia - ib; +} + +static int compareNumbersForQSortWithDouble(const void* a, const void* b) +{ + double da = *static_cast<const double*>(a); + double db = *static_cast<const double*>(b); + return (da > db) - (da < db); +} + static int compareNumbersForQSort(const void* a, const void* b) { double da = static_cast<const JSValue*>(a)->asNumber(); @@ -795,7 +971,7 @@ static int compareByStringPairForQSort(const void* a, const void* b) template<IndexingType indexingType> void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) { - ASSERT(indexingType == ArrayWithContiguous || indexingType == ArrayWithArrayStorage); + ASSERT(indexingType == ArrayWithInt32 || indexingType == ArrayWithDouble || indexingType == ArrayWithContiguous || indexingType == ArrayWithArrayStorage); unsigned lengthNotIncludingUndefined; unsigned newRelevantLength; @@ -814,11 +990,19 @@ void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallTy return; bool allValuesAreNumbers = true; - for (size_t i = 0; i < newRelevantLength; ++i) { - if (!data[i].isNumber()) { - allValuesAreNumbers = false; - break; + switch (indexingType) { + case ArrayWithInt32: + case ArrayWithDouble: + break; + + default: + for (size_t i = 0; i < newRelevantLength; ++i) { + if (!data[i].isNumber()) { + allValuesAreNumbers = false; + break; + } } + break; } if (!allValuesAreNumbers) @@ -827,7 +1011,23 @@ void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallTy // For numeric comparison, which is fast, qsort is faster than mergesort. We // also don't require mergesort's stability, since there's no user visible // side-effect from swapping the order of equal primitive values. - qsort(data, newRelevantLength, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort); + int (*compare)(const void*, const void*); + switch (indexingType) { + case ArrayWithInt32: + compare = compareNumbersForQSortWithInt32; + break; + + case ArrayWithDouble: + compare = compareNumbersForQSortWithDouble; + ASSERT(sizeof(WriteBarrier<Unknown>) == sizeof(double)); + break; + + default: + compare = compareNumbersForQSort; + break; + } + + qsort(data, newRelevantLength, sizeof(WriteBarrier<Unknown>), compare); return; } @@ -839,6 +1039,14 @@ void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType cal case ArrayClass: return; + case ArrayWithInt32: + sortNumericVector<ArrayWithInt32>(exec, compareFunction, callType, callData); + break; + + case ArrayWithDouble: + sortNumericVector<ArrayWithDouble>(exec, compareFunction, callType, callData); + break; + case ArrayWithContiguous: sortNumericVector<ArrayWithContiguous>(exec, compareFunction, callType, callData); return; @@ -854,7 +1062,7 @@ void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType cal } template<IndexingType indexingType> -void JSArray::sortCompactedVector(ExecState* exec, WriteBarrier<Unknown>* begin, unsigned relevantLength) +void JSArray::sortCompactedVector(ExecState* exec, void* begin, unsigned relevantLength) { if (!relevantLength) return; @@ -875,11 +1083,31 @@ void JSArray::sortCompactedVector(ExecState* exec, WriteBarrier<Unknown>* begin, Heap::heap(this)->pushTempSortVector(&values); bool isSortingPrimitiveValues = true; - for (size_t i = 0; i < relevantLength; i++) { - JSValue value = begin[i].get(); - ASSERT(!value.isUndefined()); - values[i].first = value; - isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive(); + switch (indexingType) { + case ArrayWithInt32: + for (size_t i = 0; i < relevantLength; i++) { + JSValue value = static_cast<WriteBarrier<Unknown>*>(begin)[i].get(); + ASSERT(value.isInt32()); + values[i].first = value; + } + break; + + case ArrayWithDouble: + for (size_t i = 0; i < relevantLength; i++) { + double value = static_cast<double*>(begin)[i]; + ASSERT(value == value); + values[i].first = JSValue(JSValue::EncodeAsDouble, value); + } + break; + + default: + for (size_t i = 0; i < relevantLength; i++) { + JSValue value = static_cast<WriteBarrier<Unknown>*>(begin)[i].get(); + ASSERT(!value.isUndefined()); + values[i].first = value; + isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive(); + } + break; } // FIXME: The following loop continues to call toString on subsequent values even after @@ -910,8 +1138,10 @@ void JSArray::sortCompactedVector(ExecState* exec, WriteBarrier<Unknown>* begin, // If the toString function changed the length of the array or vector storage, // increase the length to handle the orignal number of actual values. switch (indexingType) { + case ArrayWithInt32: + case ArrayWithDouble: case ArrayWithContiguous: - ensureContiguousLength(globalData, relevantLength); + ensureLength(globalData, relevantLength); break; case ArrayWithArrayStorage: @@ -927,8 +1157,12 @@ void JSArray::sortCompactedVector(ExecState* exec, WriteBarrier<Unknown>* begin, CRASH(); } - for (size_t i = 0; i < relevantLength; i++) - begin[i].set(globalData, this, values[i].first); + for (size_t i = 0; i < relevantLength; i++) { + if (indexingType == ArrayWithDouble) + static_cast<double*>(begin)[i] = values[i].first.asNumber(); + else + static_cast<WriteBarrier<Unknown>*>(begin)[i].set(globalData, this, values[i].first); + } Heap::heap(this)->popTempSortVector(&values); } @@ -939,8 +1173,31 @@ void JSArray::sort(ExecState* exec) switch (structure()->indexingType()) { case ArrayClass: + case ArrayWithUndecided: return; + case ArrayWithInt32: { + unsigned lengthNotIncludingUndefined; + unsigned newRelevantLength; + compactForSorting<ArrayWithInt32>( + lengthNotIncludingUndefined, newRelevantLength); + + sortCompactedVector<ArrayWithInt32>( + exec, m_butterfly->contiguousInt32(), lengthNotIncludingUndefined); + return; + } + + case ArrayWithDouble: { + unsigned lengthNotIncludingUndefined; + unsigned newRelevantLength; + compactForSorting<ArrayWithDouble>( + lengthNotIncludingUndefined, newRelevantLength); + + sortCompactedVector<ArrayWithDouble>( + exec, m_butterfly->contiguousDouble(), lengthNotIncludingUndefined); + return; + } + case ArrayWithContiguous: { unsigned lengthNotIncludingUndefined; unsigned newRelevantLength; @@ -1087,12 +1344,12 @@ void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType call unsigned numDefined = 0; unsigned numUndefined = 0; - + // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. for (; numDefined < usedVectorLength; ++numDefined) { if (numDefined > m_butterfly->vectorLength()) break; - JSValue v = currentIndexingData()[numDefined].get(); + JSValue v = getHolyIndexQuickly(numDefined); if (!v || v.isUndefined()) break; tree.abstractor().m_nodes[numDefined].value = v; @@ -1101,7 +1358,7 @@ void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType call for (unsigned i = numDefined; i < usedVectorLength; ++i) { if (i > m_butterfly->vectorLength()) break; - JSValue v = currentIndexingData()[i].get(); + JSValue v = getHolyIndexQuickly(i); if (v) { if (v.isUndefined()) ++numUndefined; @@ -1112,7 +1369,7 @@ void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType call } } } - + unsigned newUsedVectorLength = numDefined + numUndefined; // The array size may have changed. Figure out the new bounds. @@ -1127,16 +1384,31 @@ void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType call iter.start_iter_least(tree); JSGlobalData& globalData = exec->globalData(); for (unsigned i = 0; i < elementsToExtractThreshold; ++i) { - currentIndexingData()[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); + if (structure()->indexingType() == ArrayWithDouble) + butterfly()->contiguousDouble()[i] = tree.abstractor().m_nodes[*iter].value.asNumber(); + else + currentIndexingData()[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); ++iter; } // Put undefined values back in. - for (unsigned i = elementsToExtractThreshold; i < undefinedElementsThreshold; ++i) - currentIndexingData()[i].setUndefined(); + switch (structure()->indexingType()) { + case ArrayWithInt32: + case ArrayWithDouble: + ASSERT(elementsToExtractThreshold == undefinedElementsThreshold); + break; + + default: + for (unsigned i = elementsToExtractThreshold; i < undefinedElementsThreshold; ++i) + currentIndexingData()[i].setUndefined(); + } // Ensure that unused values in the vector are zeroed out. - for (unsigned i = undefinedElementsThreshold; i < clearElementsThreshold; ++i) - currentIndexingData()[i].clear(); + for (unsigned i = undefinedElementsThreshold; i < clearElementsThreshold; ++i) { + if (structure()->indexingType() == ArrayWithDouble) + butterfly()->contiguousDouble()[i] = QNaN; + else + currentIndexingData()[i].clear(); + } if (hasArrayStorage(structure()->indexingType())) arrayStorage()->m_numValuesInVector = newUsedVectorLength; @@ -1148,8 +1420,17 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, switch (structure()->indexingType()) { case ArrayClass: + case ArrayWithUndecided: return; + case ArrayWithInt32: + sortVector<ArrayWithInt32>(exec, compareFunction, callType, callData); + return; + + case ArrayWithDouble: + sortVector<ArrayWithDouble>(exec, compareFunction, callType, callData); + return; + case ArrayWithContiguous: sortVector<ArrayWithContiguous>(exec, compareFunction, callType, callData); return; @@ -1173,11 +1454,30 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) case ArrayClass: return; + case ArrayWithUndecided: { + vector = 0; + vectorEnd = 0; + break; + } + + case ArrayWithInt32: case ArrayWithContiguous: { vectorEnd = m_butterfly->publicLength(); vector = m_butterfly->contiguous(); break; } + + case ArrayWithDouble: { + vector = 0; + vectorEnd = 0; + for (; i < m_butterfly->publicLength(); ++i) { + double v = butterfly()->contiguousDouble()[i]; + if (v != v) + break; + args.append(JSValue(JSValue::EncodeAsDouble, v)); + } + break; + } case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); @@ -1216,12 +1516,31 @@ void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t le case ArrayClass: return; + case ArrayWithUndecided: { + vector = 0; + vectorEnd = 0; + break; + } + + case ArrayWithInt32: case ArrayWithContiguous: { vector = m_butterfly->contiguous(); vectorEnd = m_butterfly->publicLength(); break; } + case ArrayWithDouble: { + vector = 0; + vectorEnd = 0; + for (; i < m_butterfly->publicLength(); ++i) { + double v = m_butterfly->contiguousDouble()[i]; + if (v != v) + break; + callFrame->setArgument(i, JSValue(JSValue::EncodeAsDouble, v)); + } + break; + } + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); vector = storage->m_vector; @@ -1259,12 +1578,40 @@ void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLengt unsigned numUndefined = 0; for (; numDefined < myRelevantLength; ++numDefined) { + if (indexingType == ArrayWithInt32) { + JSValue v = m_butterfly->contiguousInt32()[numDefined].get(); + if (!v) + break; + ASSERT(v.isInt32()); + continue; + } + if (indexingType == ArrayWithDouble) { + double v = m_butterfly->contiguousDouble()[numDefined]; + if (v != v) + break; + continue; + } JSValue v = indexingData<indexingType>()[numDefined].get(); if (!v || v.isUndefined()) break; } for (unsigned i = numDefined; i < myRelevantLength; ++i) { + if (indexingType == ArrayWithInt32) { + JSValue v = m_butterfly->contiguousInt32()[i].get(); + if (!v) + continue; + ASSERT(v.isInt32()); + m_butterfly->contiguousInt32()[numDefined++].setWithoutWriteBarrier(v); + continue; + } + if (indexingType == ArrayWithDouble) { + double v = m_butterfly->contiguousDouble()[i]; + if (v != v) + continue; + m_butterfly->contiguousDouble()[numDefined++] = v; + continue; + } JSValue v = indexingData<indexingType>()[i].get(); if (v) { if (v.isUndefined()) @@ -1279,10 +1626,23 @@ void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLengt if (hasArrayStorage(indexingType)) ASSERT(!arrayStorage()->m_sparseMap); - for (unsigned i = numDefined; i < newRelevantLength; ++i) - indexingData<indexingType>()[i].setUndefined(); - for (unsigned i = newRelevantLength; i < myRelevantLength; ++i) - indexingData<indexingType>()[i].clear(); + switch (indexingType) { + case ArrayWithInt32: + case ArrayWithDouble: + ASSERT(numDefined == newRelevantLength); + break; + + default: + for (unsigned i = numDefined; i < newRelevantLength; ++i) + indexingData<indexingType>()[i].setUndefined(); + break; + } + for (unsigned i = newRelevantLength; i < myRelevantLength; ++i) { + if (indexingType == ArrayWithDouble) + m_butterfly->contiguousDouble()[i] = QNaN; + else + indexingData<indexingType>()[i].clear(); + } if (hasArrayStorage(indexingType)) arrayStorage()->m_numValuesInVector = newRelevantLength; diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h index 1d1e64173..ea1ed9047 100644 --- a/Source/JavaScriptCore/runtime/JSArray.h +++ b/Source/JavaScriptCore/runtime/JSArray.h @@ -22,7 +22,7 @@ #define JSArray_h #include "ArrayConventions.h" -#include "ButterflyInlineMethods.h" +#include "ButterflyInlines.h" #include "JSObject.h" namespace JSC { @@ -162,7 +162,7 @@ private: void sortNumericVector(ExecState*, JSValue compareFunction, CallType, const CallData&); template<IndexingType indexingType> - void sortCompactedVector(ExecState*, WriteBarrier<Unknown>* begin, unsigned relevantLength); + void sortCompactedVector(ExecState*, void* begin, unsigned relevantLength); template<IndexingType indexingType> void sortVector(ExecState*, JSValue compareFunction, CallType, const CallData&); @@ -174,13 +174,14 @@ private: void compactForSorting(unsigned& numDefined, unsigned& newRelevantLength); }; -inline Butterfly* createContiguousArrayButterfly(JSGlobalData& globalData, unsigned length) +inline Butterfly* createContiguousArrayButterfly(JSGlobalData& globalData, unsigned length, unsigned& vectorLength) { IndexingHeader header; - header.setVectorLength(std::max(length, BASE_VECTOR_LEN)); + vectorLength = std::max(length, BASE_VECTOR_LEN); + header.setVectorLength(vectorLength); header.setPublicLength(length); Butterfly* result = Butterfly::create( - globalData, 0, 0, true, header, header.vectorLength() * sizeof(EncodedJSValue)); + globalData, 0, 0, true, header, vectorLength * sizeof(EncodedJSValue)); return result; } @@ -200,13 +201,23 @@ Butterfly* createArrayButterflyInDictionaryIndexingMode(JSGlobalData&, unsigned inline JSArray* JSArray::create(JSGlobalData& globalData, Structure* structure, unsigned initialLength) { Butterfly* butterfly; - if (LIKELY(structure->indexingType() == ArrayWithContiguous)) { - butterfly = createContiguousArrayButterfly(globalData, initialLength); + if (LIKELY(!hasArrayStorage(structure->indexingType()))) { + ASSERT( + hasUndecided(structure->indexingType()) + || hasInt32(structure->indexingType()) + || hasDouble(structure->indexingType()) + || hasContiguous(structure->indexingType())); + unsigned vectorLength; + butterfly = createContiguousArrayButterfly(globalData, initialLength, vectorLength); ASSERT(initialLength < MIN_SPARSE_ARRAY_INDEX); + if (hasDouble(structure->indexingType())) { + for (unsigned i = 0; i < vectorLength; ++i) + butterfly->contiguousDouble()[i] = QNaN; + } } else { ASSERT( structure->indexingType() == ArrayWithSlowPutArrayStorage - || (initialLength && structure->indexingType() == ArrayWithArrayStorage)); + || structure->indexingType() == ArrayWithArrayStorage); butterfly = createArrayButterfly(globalData, initialLength); } JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure, butterfly); @@ -221,8 +232,13 @@ inline JSArray* JSArray::tryCreateUninitialized(JSGlobalData& globalData, Struct return 0; Butterfly* butterfly; - if (LIKELY(structure->indexingType() == ArrayWithContiguous)) { - + if (LIKELY(!hasArrayStorage(structure->indexingType()))) { + ASSERT( + hasUndecided(structure->indexingType()) + || hasInt32(structure->indexingType()) + || hasDouble(structure->indexingType()) + || hasContiguous(structure->indexingType())); + void* temp; if (!globalData.heap.tryAllocateStorage(Butterfly::totalSize(0, 0, true, vectorLength * sizeof(EncodedJSValue)), &temp)) return 0; diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp index d8f611477..bb1af9d20 100644 --- a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp +++ b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp @@ -31,8 +31,6 @@ namespace JSC { -ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSBoundFunction); - const ClassInfo JSBoundFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSBoundFunction) }; EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState* exec) @@ -88,6 +86,11 @@ JSBoundFunction* JSBoundFunction::create(ExecState* exec, JSGlobalObject* global return function; } +void JSBoundFunction::destroy(JSCell* cell) +{ + static_cast<JSBoundFunction*>(cell)->JSBoundFunction::~JSBoundFunction(); +} + bool JSBoundFunction::customHasInstance(JSObject* object, ExecState* exec, JSValue value) { return jsCast<JSBoundFunction*>(object)->m_targetFunction->hasInstance(exec, value); diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.h b/Source/JavaScriptCore/runtime/JSBoundFunction.h index 05a6ad8e1..886021940 100644 --- a/Source/JavaScriptCore/runtime/JSBoundFunction.h +++ b/Source/JavaScriptCore/runtime/JSBoundFunction.h @@ -38,6 +38,8 @@ public: typedef JSFunction Base; static JSBoundFunction* create(ExecState*, JSGlobalObject*, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int, const String&); + + static void destroy(JSCell*); static bool customHasInstance(JSObject*, ExecState*, JSValue); diff --git a/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h index 3b37613d1..b28fedd8d 100644 --- a/Source/JavaScriptCore/runtime/JSCell.h +++ b/Source/JavaScriptCore/runtime/JSCell.h @@ -28,9 +28,8 @@ #include "ConstructData.h" #include "Heap.h" #include "JSLock.h" -#include "JSValueInlineMethods.h" +#include "JSValueInlines.h" #include "SlotVisitor.h" -#include "SlotVisitorInlineMethods.h" #include "TypedArrayDescriptor.h" #include "WriteBarrier.h" #include <wtf/Noncopyable.h> diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp index 891a23930..0a98e7c13 100644 --- a/Source/JavaScriptCore/runtime/JSFunction.cpp +++ b/Source/JavaScriptCore/runtime/JSFunction.cpp @@ -48,8 +48,6 @@ EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec) return throwVMError(exec, createNotAConstructorError(exec, exec->callee())); } -ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSFunction); - const ClassInfo JSFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFunction) }; bool JSFunction::isHostFunctionNonInline() const @@ -76,10 +74,25 @@ JSFunction* JSFunction::create(ExecState* exec, JSGlobalObject* globalObject, in return function; } +void JSFunction::destroy(JSCell* cell) +{ + static_cast<JSFunction*>(cell)->JSFunction::~JSFunction(); +} + JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure) : Base(exec->globalData(), structure) , m_executable() , m_scope(exec->globalData(), this, globalObject) + // We initialize blind so that changes to the prototype after function creation but before + // the optimizer kicks in don't disable optimizations. Once the optimizer kicks in, the + // watchpoint will start watching and any changes will both force deoptimization and disable + // future attempts to optimize. This is necessary because we are guaranteed that the + // inheritorID is changed exactly once prior to optimizations kicking in. We could be + // smarter and count the number of times the prototype is clobbered and only optimize if it + // was clobbered exactly once, but that seems like overkill. In almost all cases it will be + // clobbered once, and if it's clobbered more than once, that will probably only occur + // before we started optimizing, anyway. + , m_inheritorIDWatchpoint(InitializedBlind) { } @@ -340,6 +353,7 @@ void JSFunction::put(JSCell* cell, ExecState* exec, PropertyName propertyName, J PropertySlot slot; thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot); thisObject->m_cachedInheritorID.clear(); + thisObject->m_inheritorIDWatchpoint.notifyWrite(); // Don't allow this to be cached, since a [[Put]] must clear m_cachedInheritorID. PutPropertySlot dontCache; Base::put(thisObject, exec, propertyName, value, dontCache); @@ -386,6 +400,7 @@ bool JSFunction::defineOwnProperty(JSObject* object, ExecState* exec, PropertyNa PropertySlot slot; thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot); thisObject->m_cachedInheritorID.clear(); + thisObject->m_inheritorIDWatchpoint.notifyWrite(); return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); } diff --git a/Source/JavaScriptCore/runtime/JSFunction.h b/Source/JavaScriptCore/runtime/JSFunction.h index c1f066585..322ee58e3 100644 --- a/Source/JavaScriptCore/runtime/JSFunction.h +++ b/Source/JavaScriptCore/runtime/JSFunction.h @@ -25,7 +25,9 @@ #define JSFunction_h #include "InternalFunction.h" +#include "JSDestructibleObject.h" #include "JSScope.h" +#include "Watchpoint.h" namespace JSC { @@ -46,14 +48,14 @@ namespace JSC { JS_EXPORT_PRIVATE String getCalculatedDisplayName(CallFrame*, JSObject*); - class JSFunction : public JSNonFinalObject { + class JSFunction : public JSDestructibleObject { friend class JIT; friend class DFG::SpeculativeJIT; friend class DFG::JITCompiler; friend class JSGlobalData; public: - typedef JSNonFinalObject Base; + typedef JSDestructibleObject Base; JS_EXPORT_PRIVATE static JSFunction* create(ExecState*, JSGlobalObject*, int length, const String& name, NativeFunction, Intrinsic = NoIntrinsic, NativeFunction nativeConstructor = callHostFunctionAsConstructor); @@ -66,6 +68,8 @@ namespace JSC { return function; } + static void destroy(JSCell*); + JS_EXPORT_PRIVATE String name(ExecState*); JS_EXPORT_PRIVATE String displayName(ExecState*); const String calculatedDisplayName(ExecState*); @@ -129,6 +133,21 @@ namespace JSC { return m_cachedInheritorID.get(); } + Structure* tryGetKnownInheritorID() + { + if (!m_cachedInheritorID) + return 0; + if (m_inheritorIDWatchpoint.hasBeenInvalidated()) + return 0; + return m_cachedInheritorID.get(); + } + + void addInheritorIDWatchpoint(Watchpoint* watchpoint) + { + ASSERT(tryGetKnownInheritorID()); + m_inheritorIDWatchpoint.add(watchpoint); + } + static size_t offsetOfCachedInheritorID() { return OBJECT_OFFSETOF(JSFunction, m_cachedInheritorID); @@ -169,6 +188,7 @@ namespace JSC { WriteBarrier<ExecutableBase> m_executable; WriteBarrier<JSScope> m_scope; WriteBarrier<Structure> m_cachedInheritorID; + InlineWatchpointSet m_inheritorIDWatchpoint; }; inline bool JSValue::isFunction() const diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.cpp b/Source/JavaScriptCore/runtime/JSGlobalData.cpp index 4dca5b0f6..74429c7a7 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp @@ -507,17 +507,17 @@ void JSGlobalData::dumpRegExpTrace() RTTraceList::iterator iter = ++m_rtTraceList->begin(); if (iter != m_rtTraceList->end()) { - dataLog("\nRegExp Tracing\n"); - dataLog(" match() matches\n"); - dataLog("Regular Expression JIT Address calls found\n"); - dataLog("----------------------------------------+----------------+----------+----------\n"); + dataLogF("\nRegExp Tracing\n"); + dataLogF(" match() matches\n"); + dataLogF("Regular Expression JIT Address calls found\n"); + dataLogF("----------------------------------------+----------------+----------+----------\n"); unsigned reCount = 0; for (; iter != m_rtTraceList->end(); ++iter, ++reCount) (*iter)->printTraceData(); - dataLog("%d Regular Expressions\n", reCount); + dataLogF("%d Regular Expressions\n", reCount); } m_rtTraceList->clear(); diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp index c466a2b04..6f20f0e93 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -230,9 +230,16 @@ void JSGlobalObject::reset(JSValue prototype) m_callbackObjectStructure.set(exec->globalData(), this, JSCallbackObject<JSDestructibleObject>::createStructure(exec->globalData(), this, m_objectPrototype.get())); m_arrayPrototype.set(exec->globalData(), this, ArrayPrototype::create(exec, this, ArrayPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()))); - m_arrayStructure.set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithContiguous)); - m_arrayStructureWithArrayStorage.set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithArrayStorage)); - m_arrayStructureForSlowPut.set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithSlowPutArrayStorage)); + + m_originalArrayStructureForIndexingShape[UndecidedShape >> IndexingShapeShift].set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithUndecided)); + m_originalArrayStructureForIndexingShape[Int32Shape >> IndexingShapeShift].set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithInt32)); + m_originalArrayStructureForIndexingShape[DoubleShape >> IndexingShapeShift].set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithDouble)); + m_originalArrayStructureForIndexingShape[ContiguousShape >> IndexingShapeShift].set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithContiguous)); + m_originalArrayStructureForIndexingShape[ArrayStorageShape >> IndexingShapeShift].set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithArrayStorage)); + m_originalArrayStructureForIndexingShape[SlowPutArrayStorageShape >> IndexingShapeShift].set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithSlowPutArrayStorage)); + for (unsigned i = 0; i < NumberOfIndexingShapes; ++i) + m_arrayStructureForIndexingShapeDuringAllocation[i] = m_originalArrayStructureForIndexingShape[i]; + m_regExpMatchesArrayStructure.set(exec->globalData(), this, RegExpMatchesArray::createStructure(exec->globalData(), this, m_arrayPrototype.get())); m_stringPrototype.set(exec->globalData(), this, StringPrototype::create(exec, this, StringPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()))); @@ -252,8 +259,6 @@ void JSGlobalObject::reset(JSValue prototype) m_regExpPrototype.set(exec->globalData(), this, RegExpPrototype::create(exec, this, RegExpPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()), emptyRegex)); m_regExpStructure.set(exec->globalData(), this, RegExpObject::createStructure(exec->globalData(), this, m_regExpPrototype.get())); - m_methodCallDummy.set(exec->globalData(), this, constructEmptyObject(exec)); - m_errorPrototype.set(exec->globalData(), this, ErrorPrototype::create(exec, this, ErrorPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()))); m_errorStructure.set(exec->globalData(), this, ErrorInstance::createStructure(exec->globalData(), this, m_errorPrototype.get())); @@ -360,7 +365,9 @@ inline bool hasBrokenIndexing(JSObject* object) { // This will change if we have more indexing types. IndexingType type = object->structure()->indexingType(); - return hasContiguous(type) || hasFastArrayStorage(type); + // This could be made obviously more efficient, but isn't made so right now, because + // we expect this to be an unlikely slow path anyway. + return hasUndecided(type) || hasInt32(type) || hasDouble(type) || hasContiguous(type) || hasFastArrayStorage(type); } void ObjectsWithBrokenIndexingFinder::operator()(JSCell* cell) @@ -412,8 +419,8 @@ void JSGlobalObject::haveABadTime(JSGlobalData& globalData) // Make sure that all JSArray allocations that load the appropriate structure from // this object now load a structure that uses SlowPut. - m_arrayStructure.set(globalData, this, m_arrayStructureForSlowPut.get()); - m_arrayStructureWithArrayStorage.set(globalData, this, m_arrayStructureForSlowPut.get()); + for (unsigned i = 0; i < NumberOfIndexingShapes; ++i) + m_arrayStructureForIndexingShapeDuringAllocation[i].set(globalData, this, originalArrayStructureForIndexingType(ArrayWithSlowPutArrayStorage)); // Make sure that all objects that have indexed storage switch to the slow kind of // indexed storage. @@ -428,6 +435,14 @@ void JSGlobalObject::haveABadTime(JSGlobalData& globalData) } } +bool JSGlobalObject::arrayPrototypeChainIsSane() +{ + return !hasIndexedProperties(m_arrayPrototype->structure()->indexingType()) + && m_arrayPrototype->prototype() == m_objectPrototype.get() + && !hasIndexedProperties(m_objectPrototype->structure()->indexingType()) + && m_objectPrototype->prototype().isNull(); +} + void JSGlobalObject::createThrowTypeError(ExecState* exec) { JSFunction* thrower = JSFunction::create(exec, this, 0, String(), globalFuncThrowTypeError); @@ -457,7 +472,6 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) Base::visitChildren(thisObject, visitor); visitor.append(&thisObject->m_globalThis); - visitor.append(&thisObject->m_methodCallDummy); visitor.append(&thisObject->m_regExpConstructor); visitor.append(&thisObject->m_errorConstructor); @@ -488,9 +502,10 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) visitor.append(&thisObject->m_activationStructure); visitor.append(&thisObject->m_nameScopeStructure); visitor.append(&thisObject->m_argumentsStructure); - visitor.append(&thisObject->m_arrayStructure); - visitor.append(&thisObject->m_arrayStructureWithArrayStorage); - visitor.append(&thisObject->m_arrayStructureForSlowPut); + for (unsigned i = 0; i < NumberOfIndexingShapes; ++i) + visitor.append(&thisObject->m_originalArrayStructureForIndexingShape[i]); + for (unsigned i = 0; i < NumberOfIndexingShapes; ++i) + visitor.append(&thisObject->m_arrayStructureForIndexingShapeDuringAllocation[i]); visitor.append(&thisObject->m_booleanObjectStructure); visitor.append(&thisObject->m_callbackConstructorStructure); visitor.append(&thisObject->m_callbackFunctionStructure); diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h index 3212363ab..e6edd0be7 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.h +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h @@ -22,6 +22,7 @@ #ifndef JSGlobalObject_h #define JSGlobalObject_h +#include "ArrayAllocationProfile.h" #include "JSArray.h" #include "JSGlobalData.h" #include "JSSegmentedVariableObject.h" @@ -99,7 +100,6 @@ namespace JSC { Register m_globalCallFrame[JSStack::CallFrameHeaderSize]; WriteBarrier<JSObject> m_globalThis; - WriteBarrier<JSObject> m_methodCallDummy; WriteBarrier<RegExpConstructor> m_regExpConstructor; WriteBarrier<ErrorConstructor> m_errorConstructor; @@ -130,9 +130,12 @@ namespace JSC { WriteBarrier<Structure> m_activationStructure; WriteBarrier<Structure> m_nameScopeStructure; WriteBarrier<Structure> m_argumentsStructure; - WriteBarrier<Structure> m_arrayStructure; // This gets set to m_arrayStructureForSlowPut as soon as we decide to have a bad time. - WriteBarrier<Structure> m_arrayStructureWithArrayStorage; // This gets set to m_arrayStructureForSlowPut as soon as we decide to have a bad time. - WriteBarrier<Structure> m_arrayStructureForSlowPut; + + // Lists the actual structures used for having these particular indexing shapes. + WriteBarrier<Structure> m_originalArrayStructureForIndexingShape[NumberOfIndexingShapes]; + // Lists the structures we should use during allocation for these particular indexing shapes. + WriteBarrier<Structure> m_arrayStructureForIndexingShapeDuringAllocation[NumberOfIndexingShapes]; + WriteBarrier<Structure> m_booleanObjectStructure; WriteBarrier<Structure> m_callbackConstructorStructure; WriteBarrier<Structure> m_callbackFunctionStructure; @@ -268,21 +271,31 @@ namespace JSC { RegExpPrototype* regExpPrototype() const { return m_regExpPrototype.get(); } ErrorPrototype* errorPrototype() const { return m_errorPrototype.get(); } - JSObject* methodCallDummy() const { return m_methodCallDummy.get(); } - Structure* withScopeStructure() const { return m_withScopeStructure.get(); } Structure* strictEvalActivationStructure() const { return m_strictEvalActivationStructure.get(); } Structure* activationStructure() const { return m_activationStructure.get(); } Structure* nameScopeStructure() const { return m_nameScopeStructure.get(); } Structure* argumentsStructure() const { return m_argumentsStructure.get(); } - Structure* arrayStructure() const { return m_arrayStructure.get(); } - Structure* arrayStructureWithArrayStorage() const { return m_arrayStructureWithArrayStorage.get(); } - void* addressOfArrayStructure() { return &m_arrayStructure; } - void* addressOfArrayStructureWithArrayStorage() { return &m_arrayStructureWithArrayStorage; } + Structure* originalArrayStructureForIndexingType(IndexingType indexingType) const + { + ASSERT(indexingType & IsArray); + return m_originalArrayStructureForIndexingShape[(indexingType & IndexingShapeMask) >> IndexingShapeShift].get(); + } + Structure* arrayStructureForIndexingTypeDuringAllocation(IndexingType indexingType) const + { + ASSERT(indexingType & IsArray); + return m_arrayStructureForIndexingShapeDuringAllocation[(indexingType & IndexingShapeMask) >> IndexingShapeShift].get(); + } + Structure* arrayStructureForProfileDuringAllocation(ArrayAllocationProfile* profile) const + { + return arrayStructureForIndexingTypeDuringAllocation(ArrayAllocationProfile::selectIndexingTypeFor(profile)); + } + bool isOriginalArrayStructure(Structure* structure) { - return structure == m_arrayStructure.get() || structure == m_arrayStructureWithArrayStorage.get(); + return originalArrayStructureForIndexingType(structure->indexingType() | IsArray) == structure; } + Structure* booleanObjectStructure() const { return m_booleanObjectStructure.get(); } Structure* callbackConstructorStructure() const { return m_callbackConstructorStructure.get(); } Structure* callbackFunctionStructure() const { return m_callbackFunctionStructure.get(); } @@ -317,6 +330,8 @@ namespace JSC { } void haveABadTime(JSGlobalData&); + + bool arrayPrototypeChainIsSane(); void setProfileGroup(unsigned value) { createRareDataIfNeeded(); m_rareData->profileGroup = value; } unsigned profileGroup() const @@ -450,22 +465,27 @@ namespace JSC { return prototypeForLookup(exec->lexicalGlobalObject()); } - inline StructureChain* Structure::prototypeChain(ExecState* exec) const + inline StructureChain* Structure::prototypeChain(JSGlobalData& globalData, JSGlobalObject* globalObject) const { // We cache our prototype chain so our clients can share it. - if (!isValid(exec, m_cachedPrototypeChain.get())) { - JSValue prototype = prototypeForLookup(exec); - m_cachedPrototypeChain.set(exec->globalData(), this, StructureChain::create(exec->globalData(), prototype.isNull() ? 0 : asObject(prototype)->structure())); + if (!isValid(globalObject, m_cachedPrototypeChain.get())) { + JSValue prototype = prototypeForLookup(globalObject); + m_cachedPrototypeChain.set(globalData, this, StructureChain::create(globalData, prototype.isNull() ? 0 : asObject(prototype)->structure())); } return m_cachedPrototypeChain.get(); } - inline bool Structure::isValid(ExecState* exec, StructureChain* cachedPrototypeChain) const + inline StructureChain* Structure::prototypeChain(ExecState* exec) const + { + return prototypeChain(exec->globalData(), exec->lexicalGlobalObject()); + } + + inline bool Structure::isValid(JSGlobalObject* globalObject, StructureChain* cachedPrototypeChain) const { if (!cachedPrototypeChain) return false; - JSValue prototype = prototypeForLookup(exec); + JSValue prototype = prototypeForLookup(globalObject); WriteBarrier<Structure>* cachedStructure = cachedPrototypeChain->head(); while(*cachedStructure && !prototype.isNull()) { if (asObject(prototype)->structure() != cachedStructure->get()) @@ -476,6 +496,11 @@ namespace JSC { return prototype.isNull() && !*cachedStructure; } + inline bool Structure::isValid(ExecState* exec, StructureChain* cachedPrototypeChain) const + { + return isValid(exec->lexicalGlobalObject(), cachedPrototypeChain); + } + inline JSGlobalObject* ExecState::dynamicGlobalObject() { if (this == lexicalGlobalObject()->globalExec()) @@ -497,34 +522,34 @@ namespace JSC { return constructEmptyObject(exec, exec->lexicalGlobalObject()); } - inline JSArray* constructEmptyArray(ExecState* exec, JSGlobalObject* globalObject, unsigned initialLength = 0) + inline JSArray* constructEmptyArray(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, unsigned initialLength = 0) { - return JSArray::create(exec->globalData(), initialLength >= MIN_SPARSE_ARRAY_INDEX ? globalObject->arrayStructureWithArrayStorage() : globalObject->arrayStructure(), initialLength); + return ArrayAllocationProfile::updateLastAllocationFor(profile, JSArray::create(exec->globalData(), initialLength >= MIN_SPARSE_ARRAY_INDEX ? globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage) : globalObject->arrayStructureForProfileDuringAllocation(profile), initialLength)); } - inline JSArray* constructEmptyArray(ExecState* exec, unsigned initialLength = 0) + inline JSArray* constructEmptyArray(ExecState* exec, ArrayAllocationProfile* profile, unsigned initialLength = 0) { - return constructEmptyArray(exec, exec->lexicalGlobalObject(), initialLength); + return constructEmptyArray(exec, profile, exec->lexicalGlobalObject(), initialLength); } - inline JSArray* constructArray(ExecState* exec, JSGlobalObject* globalObject, const ArgList& values) + inline JSArray* constructArray(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, const ArgList& values) { - return constructArray(exec, globalObject->arrayStructure(), values); + return ArrayAllocationProfile::updateLastAllocationFor(profile, constructArray(exec, globalObject->arrayStructureForProfileDuringAllocation(profile), values)); } - inline JSArray* constructArray(ExecState* exec, const ArgList& values) + inline JSArray* constructArray(ExecState* exec, ArrayAllocationProfile* profile, const ArgList& values) { - return constructArray(exec, exec->lexicalGlobalObject(), values); + return constructArray(exec, profile, exec->lexicalGlobalObject(), values); } - inline JSArray* constructArray(ExecState* exec, JSGlobalObject* globalObject, const JSValue* values, unsigned length) + inline JSArray* constructArray(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, const JSValue* values, unsigned length) { - return constructArray(exec, globalObject->arrayStructure(), values, length); + return ArrayAllocationProfile::updateLastAllocationFor(profile, constructArray(exec, globalObject->arrayStructureForProfileDuringAllocation(profile), values, length)); } - inline JSArray* constructArray(ExecState* exec, const JSValue* values, unsigned length) + inline JSArray* constructArray(ExecState* exec, ArrayAllocationProfile* profile, const JSValue* values, unsigned length) { - return constructArray(exec, exec->lexicalGlobalObject(), values, length); + return constructArray(exec, profile, exec->lexicalGlobalObject(), values, length); } class DynamicGlobalObjectScope { diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp index 7ac76d350..1010ad2b7 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -52,7 +52,7 @@ namespace JSC { static JSValue encode(ExecState* exec, const char* doNotEscape) { - CString cstr = exec->argument(0).toString(exec)->value(exec).utf8(true); + CString cstr = exec->argument(0).toString(exec)->value(exec).utf8(String::StrictConversion); if (!cstr.data()) return throwError(exec, createURIError(exec, ASCIILiteral("String contained an illegal UTF-16 sequence."))); diff --git a/Source/JavaScriptCore/runtime/JSLock.cpp b/Source/JavaScriptCore/runtime/JSLock.cpp index 85dbdfedb..5e3d12c92 100644 --- a/Source/JavaScriptCore/runtime/JSLock.cpp +++ b/Source/JavaScriptCore/runtime/JSLock.cpp @@ -74,7 +74,9 @@ JSLockHolder::~JSLockHolder() } JSLock::JSLock() - : m_lockCount(0) + : m_ownerThread(0) + , m_lockCount(0) + , m_lockDropDepth(0) { m_spinLock.Init(); } diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index 6a3fb84e4..564093e33 100644 --- a/Source/JavaScriptCore/runtime/JSObject.cpp +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -24,14 +24,14 @@ #include "config.h" #include "JSObject.h" -#include "ButterflyInlineMethods.h" -#include "CopiedSpaceInlineMethods.h" +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" #include "CopyVisitor.h" -#include "CopyVisitorInlineMethods.h" +#include "CopyVisitorInlines.h" #include "DatePrototype.h" #include "ErrorConstructor.h" #include "GetterSetter.h" -#include "IndexingHeaderInlineMethods.h" +#include "IndexingHeaderInlines.h" #include "JSFunction.h" #include "JSGlobalObject.h" #include "Lookup.h" @@ -42,7 +42,7 @@ #include "PropertyDescriptor.h" #include "PropertyNameArray.h" #include "Reject.h" -#include "SlotVisitorInlineMethods.h" +#include "SlotVisitorInlines.h" #include <math.h> #include <wtf/Assertions.h> @@ -129,7 +129,16 @@ ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butt size_t count; switch (structure->indexingType()) { - case ALL_CONTIGUOUS_INDEXING_TYPES: { + case ALL_UNDECIDED_INDEXING_TYPES: { + currentTarget = 0; + currentSource = 0; + count = 0; + break; + } + + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: { currentTarget = newButterfly->contiguous(); currentSource = butterfly->contiguous(); ASSERT(newButterfly->publicLength() <= newButterfly->vectorLength()); @@ -152,8 +161,7 @@ ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butt break; } - while (count--) - (currentTarget++)->setWithoutWriteBarrier((currentSource++)->get()); + memcpy(currentTarget, currentSource, count * sizeof(EncodedJSValue)); } m_butterfly = newButterfly; @@ -272,8 +280,10 @@ bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned switch (thisObject->structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: break; + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: { Butterfly* butterfly = thisObject->m_butterfly; if (i >= butterfly->vectorLength()) @@ -288,6 +298,20 @@ bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned return false; } + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->m_butterfly; + if (i >= butterfly->vectorLength()) + return false; + + double value = butterfly->contiguousDouble()[i]; + if (value == value) { + slot.setValue(JSValue(JSValue::EncodeAsDouble, value)); + return true; + } + + return false; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); if (i >= storage->length()) @@ -332,26 +356,30 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV putByIndex(thisObject, exec, i, value, slot.isStrictMode()); return; } - + // Check if there are any setters or getters in the prototype chain JSValue prototype; if (propertyName != exec->propertyNames().underscoreProto) { for (JSObject* obj = thisObject; !obj->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) { prototype = obj->prototype(); if (prototype.isNull()) { - if (!thisObject->putDirectInternal<PutModePut>(globalData, propertyName, value, 0, slot, getCallableObject(value)) && slot.isStrictMode()) + ASSERT(!thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->globalData(), propertyName)); + if (!thisObject->putDirectInternal<PutModePut>(globalData, propertyName, value, 0, slot, getCallableObject(value)) + && slot.isStrictMode()) throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); return; } } } - for (JSObject* obj = thisObject; ; obj = asObject(prototype)) { + JSObject* obj; + for (obj = thisObject; ; obj = asObject(prototype)) { unsigned attributes; JSCell* specificValue; PropertyOffset offset = obj->structure()->get(globalData, propertyName, attributes, specificValue); if (isValidOffset(offset)) { if (attributes & ReadOnly) { + ASSERT(thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->globalData(), propertyName) || obj == thisObject); if (slot.isStrictMode()) throwError(exec, createTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError))); return; @@ -359,6 +387,8 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV JSValue gs = obj->getDirectOffset(offset); if (gs.isGetterSetter()) { + ASSERT(attributes & Accessor); + ASSERT(thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->globalData(), propertyName) || obj == thisObject); JSObject* setterFunc = asGetterSetter(gs)->setter(); if (!setterFunc) { if (slot.isStrictMode()) @@ -374,7 +404,8 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV // If this is WebCore's global object then we need to substitute the shell. call(exec, setterFunc, callType, callData, thisObject->methodTable()->toThisObject(thisObject, exec), args); return; - } + } else + ASSERT(!(attributes & Accessor)); // If there's an existing property on the object or one of its // prototypes it should be replaced, so break here. @@ -386,6 +417,7 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV break; } + ASSERT(!thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->globalData(), propertyName) || obj == thisObject); if (!thisObject->putDirectInternal<PutModePut>(globalData, propertyName, value, 0, slot, getCallableObject(value)) && slot.isStrictMode()) throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); return; @@ -405,6 +437,22 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, case ALL_BLANK_INDEXING_TYPES: break; + case ALL_UNDECIDED_INDEXING_TYPES: { + thisObject->convertUndecidedForValue(exec->globalData(), value); + // Reloop. + putByIndex(cell, exec, propertyName, value, shouldThrow); + return; + } + + case ALL_INT32_INDEXING_TYPES: { + if (!value.isInt32()) { + thisObject->convertInt32ForValue(exec->globalData(), value); + putByIndex(cell, exec, propertyName, value, shouldThrow); + return; + } + // Fall through. + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { Butterfly* butterfly = thisObject->m_butterfly; if (propertyName >= butterfly->vectorLength()) @@ -415,6 +463,29 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, return; } + case ALL_DOUBLE_INDEXING_TYPES: { + if (!value.isNumber()) { + thisObject->convertDoubleToContiguous(exec->globalData()); + // Reloop. + putByIndex(cell, exec, propertyName, value, shouldThrow); + return; + } + double valueAsDouble = value.asNumber(); + if (valueAsDouble != valueAsDouble) { + thisObject->convertDoubleToContiguous(exec->globalData()); + // Reloop. + putByIndex(cell, exec, propertyName, value, shouldThrow); + return; + } + Butterfly* butterfly = thisObject->m_butterfly; + if (propertyName >= butterfly->vectorLength()) + break; + butterfly->contiguousDouble()[propertyName] = valueAsDouble; + if (propertyName >= butterfly->publicLength()) + butterfly->setPublicLength(propertyName + 1); + return; + } + case NonArrayWithArrayStorage: case ArrayWithArrayStorage: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); @@ -507,10 +578,13 @@ ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists void JSObject::enterDictionaryIndexingMode(JSGlobalData& globalData) { switch (structure()->indexingType()) { + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize // this case if we ever cared. - enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertContiguousToArrayStorage(globalData)); + enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, ensureArrayStorageSlow(globalData)); break; case ALL_ARRAY_STORAGE_INDEXING_TYPES: enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); @@ -534,7 +608,7 @@ void JSObject::notifyPresenceOfIndexedAccessors(JSGlobalData& globalData) globalObject()->haveABadTime(globalData); } -WriteBarrier<Unknown>* JSObject::createInitialContiguous(JSGlobalData& globalData, unsigned length) +Butterfly* JSObject::createInitialIndexedStorage(JSGlobalData& globalData, unsigned length, size_t elementSize) { ASSERT(length < MAX_ARRAY_INDEX); IndexingType oldType = structure()->indexingType(); @@ -544,9 +618,41 @@ WriteBarrier<Unknown>* JSObject::createInitialContiguous(JSGlobalData& globalDat unsigned vectorLength = std::max(length, BASE_VECTOR_LEN); Butterfly* newButterfly = m_butterfly->growArrayRight( globalData, structure(), structure()->outOfLineCapacity(), false, 0, - sizeof(EncodedJSValue) * vectorLength); + elementSize * vectorLength); newButterfly->setPublicLength(length); newButterfly->setVectorLength(vectorLength); + return newButterfly; +} + +Butterfly* JSObject::createInitialUndecided(JSGlobalData& globalData, unsigned length) +{ + Butterfly* newButterfly = createInitialIndexedStorage(globalData, length, sizeof(EncodedJSValue)); + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateUndecided); + setButterfly(globalData, newButterfly, newStructure); + return newButterfly; +} + +WriteBarrier<Unknown>* JSObject::createInitialInt32(JSGlobalData& globalData, unsigned length) +{ + Butterfly* newButterfly = createInitialIndexedStorage(globalData, length, sizeof(EncodedJSValue)); + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateInt32); + setButterfly(globalData, newButterfly, newStructure); + return newButterfly->contiguousInt32(); +} + +double* JSObject::createInitialDouble(JSGlobalData& globalData, unsigned length) +{ + Butterfly* newButterfly = createInitialIndexedStorage(globalData, length, sizeof(double)); + for (unsigned i = newButterfly->vectorLength(); i--;) + newButterfly->contiguousDouble()[i] = QNaN; + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateDouble); + setButterfly(globalData, newButterfly, newStructure); + return newButterfly->contiguousDouble(); +} + +WriteBarrier<Unknown>* JSObject::createInitialContiguous(JSGlobalData& globalData, unsigned length) +{ + Butterfly* newButterfly = createInitialIndexedStorage(globalData, length, sizeof(EncodedJSValue)); Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateContiguous); setButterfly(globalData, newButterfly, newStructure); return newButterfly->contiguous(); @@ -577,10 +683,33 @@ ArrayStorage* JSObject::createInitialArrayStorage(JSGlobalData& globalData) return createArrayStorage(globalData, 0, BASE_VECTOR_LEN); } -ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength) +WriteBarrier<Unknown>* JSObject::convertUndecidedToInt32(JSGlobalData& globalData) { - ASSERT(hasContiguous(structure()->indexingType())); + ASSERT(hasUndecided(structure()->indexingType())); + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateInt32)); + return m_butterfly->contiguousInt32(); +} + +double* JSObject::convertUndecidedToDouble(JSGlobalData& globalData) +{ + ASSERT(hasUndecided(structure()->indexingType())); + + for (unsigned i = m_butterfly->vectorLength(); i--;) + m_butterfly->contiguousDouble()[i] = QNaN; + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateDouble)); + return m_butterfly->contiguousDouble(); +} + +WriteBarrier<Unknown>* JSObject::convertUndecidedToContiguous(JSGlobalData& globalData) +{ + ASSERT(hasUndecided(structure()->indexingType())); + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateContiguous)); + return m_butterfly->contiguous(); +} + +ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(JSGlobalData& globalData, unsigned neededLength) +{ unsigned publicLength = m_butterfly->publicLength(); unsigned propertyCapacity = structure()->outOfLineCapacity(); unsigned propertySize = structure()->outOfLineSize(); @@ -599,7 +728,66 @@ ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData newStorage->m_sparseMap.clear(); newStorage->m_indexBias = 0; newStorage->m_numValuesInVector = 0; - for (unsigned i = publicLength; i--;) { + + return newStorage; +} + +ArrayStorage* JSObject::convertUndecidedToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength) +{ + ASSERT(hasUndecided(structure()->indexingType())); + + ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(globalData, neededLength); + // No need to copy elements. + + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), transition); + setButterfly(globalData, storage->butterfly(), newStructure); + return storage; +} + +ArrayStorage* JSObject::convertUndecidedToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition) +{ + return convertUndecidedToArrayStorage(globalData, transition, m_butterfly->vectorLength()); +} + +ArrayStorage* JSObject::convertUndecidedToArrayStorage(JSGlobalData& globalData) +{ + return convertUndecidedToArrayStorage(globalData, structure()->suggestedArrayStorageTransition()); +} + +double* JSObject::convertInt32ToDouble(JSGlobalData& globalData) +{ + ASSERT(hasInt32(structure()->indexingType())); + + for (unsigned i = m_butterfly->vectorLength(); i--;) { + WriteBarrier<Unknown>* current = &m_butterfly->contiguousInt32()[i]; + double* currentAsDouble = bitwise_cast<double*>(current); + JSValue v = current->get(); + if (!v) { + *currentAsDouble = QNaN; + continue; + } + ASSERT(v.isInt32()); + *currentAsDouble = v.asInt32(); + } + + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateDouble)); + return m_butterfly->contiguousDouble(); +} + +WriteBarrier<Unknown>* JSObject::convertInt32ToContiguous(JSGlobalData& globalData) +{ + ASSERT(hasInt32(structure()->indexingType())); + + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateContiguous)); + return m_butterfly->contiguous(); +} + +ArrayStorage* JSObject::convertInt32ToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength) +{ + ASSERT(hasInt32(structure()->indexingType())); + + ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(globalData, neededLength); + for (unsigned i = m_butterfly->publicLength(); i--;) { JSValue v = m_butterfly->contiguous()[i].get(); if (!v) continue; @@ -608,7 +796,82 @@ ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData } Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), transition); - setButterfly(globalData, newButterfly, newStructure); + setButterfly(globalData, newStorage->butterfly(), newStructure); + return newStorage; +} + +ArrayStorage* JSObject::convertInt32ToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition) +{ + return convertInt32ToArrayStorage(globalData, transition, m_butterfly->vectorLength()); +} + +ArrayStorage* JSObject::convertInt32ToArrayStorage(JSGlobalData& globalData) +{ + return convertInt32ToArrayStorage(globalData, structure()->suggestedArrayStorageTransition()); +} + +WriteBarrier<Unknown>* JSObject::convertDoubleToContiguous(JSGlobalData& globalData) +{ + ASSERT(hasDouble(structure()->indexingType())); + + for (unsigned i = m_butterfly->vectorLength(); i--;) { + double* current = &m_butterfly->contiguousDouble()[i]; + WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current); + double value = *current; + if (value != value) { + currentAsValue->clear(); + continue; + } + currentAsValue->setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value)); + } + + setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateContiguous)); + return m_butterfly->contiguous(); +} + +ArrayStorage* JSObject::convertDoubleToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength) +{ + ASSERT(hasDouble(structure()->indexingType())); + + ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(globalData, neededLength); + for (unsigned i = m_butterfly->publicLength(); i--;) { + double value = m_butterfly->contiguousDouble()[i]; + if (value != value) + continue; + newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value)); + newStorage->m_numValuesInVector++; + } + + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), transition); + setButterfly(globalData, newStorage->butterfly(), newStructure); + return newStorage; +} + +ArrayStorage* JSObject::convertDoubleToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition) +{ + return convertDoubleToArrayStorage(globalData, transition, m_butterfly->vectorLength()); +} + +ArrayStorage* JSObject::convertDoubleToArrayStorage(JSGlobalData& globalData) +{ + return convertDoubleToArrayStorage(globalData, structure()->suggestedArrayStorageTransition()); +} + +ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength) +{ + ASSERT(hasContiguous(structure()->indexingType())); + + ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(globalData, neededLength); + for (unsigned i = m_butterfly->publicLength(); i--;) { + JSValue v = m_butterfly->contiguous()[i].get(); + if (!v) + continue; + newStorage->m_vector[i].setWithoutWriteBarrier(v); + newStorage->m_numValuesInVector++; + } + + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), transition); + setButterfly(globalData, newStorage->butterfly(), newStructure); return newStorage; } @@ -622,48 +885,154 @@ ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData return convertContiguousToArrayStorage(globalData, structure()->suggestedArrayStorageTransition()); } -WriteBarrier<Unknown>* JSObject::ensureContiguousSlow(JSGlobalData& globalData) +void JSObject::convertUndecidedForValue(JSGlobalData& globalData, JSValue value) +{ + if (value.isInt32()) { + convertUndecidedToInt32(globalData); + return; + } + + if (value.isDouble()) { + convertUndecidedToDouble(globalData); + return; + } + + convertUndecidedToContiguous(globalData); +} + +void JSObject::convertInt32ForValue(JSGlobalData& globalData, JSValue value) +{ + ASSERT(!value.isInt32()); + + if (value.isDouble()) { + convertInt32ToDouble(globalData); + return; + } + + convertInt32ToContiguous(globalData); +} + +void JSObject::setIndexQuicklyToUndecided(JSGlobalData& globalData, unsigned index, JSValue value) +{ + ASSERT(index < m_butterfly->publicLength()); + ASSERT(index < m_butterfly->vectorLength()); + convertUndecidedForValue(globalData, value); + setIndexQuickly(globalData, index, value); +} + +void JSObject::convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(JSGlobalData& globalData, unsigned index, JSValue value) +{ + ASSERT(!value.isInt32()); + convertInt32ForValue(globalData, value); + setIndexQuickly(globalData, index, value); +} + +void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(JSGlobalData& globalData, unsigned index, JSValue value) +{ + ASSERT(!value.isNumber() || value.asNumber() != value.asNumber()); + convertDoubleToContiguous(globalData); + setIndexQuickly(globalData, index, value); +} + +WriteBarrier<Unknown>* JSObject::ensureInt32Slow(JSGlobalData& globalData) { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) return 0; - return createInitialContiguous(globalData, 0); + return createInitialInt32(globalData, 0); + + case ALL_UNDECIDED_INDEXING_TYPES: + return convertUndecidedToInt32(globalData); + + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return 0; default: - ASSERT_NOT_REACHED(); + CRASH(); return 0; } } -ArrayStorage* JSObject::ensureArrayStorageSlow(JSGlobalData& globalData) +double* JSObject::ensureDoubleSlow(JSGlobalData& globalData) { switch (structure()->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) + return 0; + return createInitialDouble(globalData, 0); + + case ALL_UNDECIDED_INDEXING_TYPES: + return convertUndecidedToDouble(globalData); + + case ALL_INT32_INDEXING_TYPES: + return convertInt32ToDouble(globalData); + case ALL_CONTIGUOUS_INDEXING_TYPES: - ASSERT(!indexingShouldBeSparse()); - ASSERT(!structure()->needsSlowPutIndexing()); - return convertContiguousToArrayStorage(globalData); + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return 0; + default: + CRASH(); + return 0; + } +} + +WriteBarrier<Unknown>* JSObject::ensureContiguousSlow(JSGlobalData& globalData) +{ + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: - if (UNLIKELY(indexingShouldBeSparse())) - return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData); - return createInitialArrayStorage(globalData); + if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) + return 0; + return createInitialContiguous(globalData, 0); + + case ALL_UNDECIDED_INDEXING_TYPES: + return convertUndecidedToContiguous(globalData); + + case ALL_INT32_INDEXING_TYPES: + return convertInt32ToContiguous(globalData); + + case ALL_DOUBLE_INDEXING_TYPES: + return convertDoubleToContiguous(globalData); + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return 0; default: - ASSERT_NOT_REACHED(); + CRASH(); return 0; } } -Butterfly* JSObject::ensureIndexedStorageSlow(JSGlobalData& globalData) +ArrayStorage* JSObject::ensureArrayStorageSlow(JSGlobalData& globalData) { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: - if (UNLIKELY(structure()->needsSlowPutIndexing())) - return createInitialArrayStorage(globalData)->butterfly(); if (UNLIKELY(indexingShouldBeSparse())) - return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)->butterfly(); - return Butterfly::fromContiguous(createInitialContiguous(globalData, 0)); + return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData); + return createInitialArrayStorage(globalData); + + case ALL_UNDECIDED_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure()->needsSlowPutIndexing()); + return convertUndecidedToArrayStorage(globalData); + + case ALL_INT32_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure()->needsSlowPutIndexing()); + return convertInt32ToArrayStorage(globalData); + + case ALL_DOUBLE_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure()->needsSlowPutIndexing()); + return convertDoubleToArrayStorage(globalData); + + case ALL_CONTIGUOUS_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure()->needsSlowPutIndexing()); + return convertContiguousToArrayStorage(globalData); default: ASSERT_NOT_REACHED(); @@ -674,13 +1043,6 @@ Butterfly* JSObject::ensureIndexedStorageSlow(JSGlobalData& globalData) ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData& globalData) { switch (structure()->indexingType()) { - case ALL_CONTIGUOUS_INDEXING_TYPES: - // FIXME: This could be made way more efficient, if we cared. - return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertContiguousToArrayStorage(globalData)); - - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); - case ALL_BLANK_INDEXING_TYPES: { createArrayStorage(globalData, 0, 0); SparseArrayValueMap* map = allocateSparseIndexMap(globalData); @@ -688,8 +1050,23 @@ ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(J return arrayStorage(); } + case ALL_UNDECIDED_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertUndecidedToArrayStorage(globalData)); + + case ALL_INT32_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertInt32ToArrayStorage(globalData)); + + case ALL_DOUBLE_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertDoubleToArrayStorage(globalData)); + + case ALL_CONTIGUOUS_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertContiguousToArrayStorage(globalData)); + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); + default: - ASSERT_NOT_REACHED(); + CRASH(); return 0; } } @@ -697,10 +1074,21 @@ ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(J void JSObject::switchToSlowPutArrayStorage(JSGlobalData& globalData) { switch (structure()->indexingType()) { - case ALL_CONTIGUOUS_INDEXING_TYPES: { + case ALL_UNDECIDED_INDEXING_TYPES: + convertUndecidedToArrayStorage(globalData, AllocateSlowPutArrayStorage); + break; + + case ALL_INT32_INDEXING_TYPES: + convertInt32ToArrayStorage(globalData, AllocateSlowPutArrayStorage); + break; + + case ALL_DOUBLE_INDEXING_TYPES: + convertDoubleToArrayStorage(globalData, AllocateSlowPutArrayStorage); + break; + + case ALL_CONTIGUOUS_INDEXING_TYPES: convertContiguousToArrayStorage(globalData, AllocateSlowPutArrayStorage); break; - } case NonArrayWithArrayStorage: case ArrayWithArrayStorage: { @@ -710,7 +1098,7 @@ void JSObject::switchToSlowPutArrayStorage(JSGlobalData& globalData) } default: - ASSERT_NOT_REACHED(); + CRASH(); break; } } @@ -877,8 +1265,10 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) switch (thisObject->structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: return true; + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: { Butterfly* butterfly = thisObject->m_butterfly; if (i >= butterfly->vectorLength()) @@ -887,6 +1277,14 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) return true; } + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->m_butterfly; + if (i >= butterfly->vectorLength()) + return true; + butterfly->contiguousDouble()[i] = QNaN; + return true; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); @@ -1058,8 +1456,10 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa // which almost certainly means a different structure for PropertyNameArray. switch (object->structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: break; + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: { Butterfly* butterfly = object->m_butterfly; unsigned usedLength = butterfly->publicLength(); @@ -1071,6 +1471,18 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa break; } + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = object->m_butterfly; + unsigned usedLength = butterfly->publicLength(); + for (unsigned i = 0; i < usedLength; ++i) { + double value = butterfly->contiguousDouble()[i]; + if (value != value) + continue; + propertyNames.add(Identifier::from(exec, i)); + } + break; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = object->m_butterfly->arrayStorage(); @@ -1460,9 +1872,10 @@ bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, J return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow); } -void JSObject::putByIndexBeyondVectorLengthContiguousWithoutAttributes(ExecState* exec, unsigned i, JSValue value) +template<IndexingType indexingShape> +void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, unsigned i, JSValue value) { - ASSERT(hasContiguous(structure()->indexingType())); + ASSERT((structure()->indexingType() & IndexingShapeMask) == indexingShape); ASSERT(!indexingShouldBeSparse()); // For us to get here, the index is either greater than the public length, or greater than @@ -1473,9 +1886,9 @@ void JSObject::putByIndexBeyondVectorLengthContiguousWithoutAttributes(ExecState if (i >= MAX_ARRAY_INDEX - 1 || (i >= MIN_SPARSE_ARRAY_INDEX - && !isDenseEnoughForVector(i, countElementsInContiguous(m_butterfly)))) { + && !isDenseEnoughForVector(i, countElements<indexingShape>(m_butterfly)))) { ASSERT(i <= MAX_ARRAY_INDEX); - convertContiguousToArrayStorage(globalData, AllocateArrayStorage); + ensureArrayStorageSlow(globalData); SparseArrayValueMap* map = allocateSparseIndexMap(globalData); map->putEntry(exec, this, i, value, false); ASSERT(i >= arrayStorage()->length()); @@ -1483,10 +1896,30 @@ void JSObject::putByIndexBeyondVectorLengthContiguousWithoutAttributes(ExecState return; } - ensureContiguousLength(globalData, i + 1); + ensureLength(globalData, i + 1); ASSERT(i < m_butterfly->vectorLength()); - m_butterfly->contiguous()[i].set(globalData, this, value); + switch (indexingShape) { + case Int32Shape: + ASSERT(value.isInt32()); + m_butterfly->contiguousInt32()[i].setWithoutWriteBarrier(value); + break; + + case DoubleShape: { + ASSERT(value.isNumber()); + double valueAsDouble = value.asNumber(); + ASSERT(valueAsDouble == valueAsDouble); + m_butterfly->contiguousDouble()[i] = valueAsDouble; + break; + } + + case ContiguousShape: + m_butterfly->contiguous()[i].set(globalData, this, value); + break; + + default: + CRASH(); + } } void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage) @@ -1592,8 +2025,23 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue break; } + case ALL_UNDECIDED_INDEXING_TYPES: { + CRASH(); + break; + } + + case ALL_INT32_INDEXING_TYPES: { + putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value); + break; + } + + case ALL_DOUBLE_INDEXING_TYPES: { + putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value); + break; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { - putByIndexBeyondVectorLengthContiguousWithoutAttributes(exec, i, value); + putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value); break; } @@ -1724,12 +2172,49 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV return true; } + case ALL_UNDECIDED_INDEXING_TYPES: { + convertUndecidedForValue(exec->globalData(), value); + // Reloop. + return putDirectIndex(exec, i, value, attributes, mode); + } + + case ALL_INT32_INDEXING_TYPES: { + if (attributes & (ReadOnly | Accessor)) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, convertInt32ToArrayStorage(globalData)); + } + if (!value.isInt32()) { + convertInt32ForValue(globalData, value); + return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode); + } + putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value); + return true; + } + + case ALL_DOUBLE_INDEXING_TYPES: { + if (attributes & (ReadOnly | Accessor)) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, convertDoubleToArrayStorage(globalData)); + } + if (!value.isNumber()) { + convertDoubleToContiguous(globalData); + return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode); + } + double valueAsDouble = value.asNumber(); + if (valueAsDouble != valueAsDouble) { + convertDoubleToContiguous(globalData); + return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode); + } + putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value); + return true; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { if (attributes & (ReadOnly | Accessor)) { return putDirectIndexBeyondVectorLengthWithArrayStorage( exec, i, value, attributes, mode, convertContiguousToArrayStorage(globalData)); } - putByIndexBeyondVectorLengthContiguousWithoutAttributes(exec, i, value); + putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value); return true; } @@ -1769,33 +2254,65 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength) unsigned vectorLength; unsigned length; - switch (structure()->indexingType()) { - case ALL_BLANK_INDEXING_TYPES: - vectorLength = 0; - length = 0; - break; - case ALL_CONTIGUOUS_INDEXING_TYPES: - case ALL_ARRAY_STORAGE_INDEXING_TYPES: + if (hasIndexedProperties(structure()->indexingType())) { vectorLength = m_butterfly->vectorLength(); length = m_butterfly->publicLength(); - break; - default: - CRASH(); - return 0; + } else { + vectorLength = 0; + length = 0; } + return getNewVectorLength(vectorLength, length, desiredLength); } -unsigned JSObject::countElementsInContiguous(Butterfly* butterfly) +template<IndexingType indexingShape> +unsigned JSObject::countElements(Butterfly* butterfly) { unsigned numValues = 0; for (unsigned i = butterfly->publicLength(); i--;) { - if (butterfly->contiguous()[i]) - numValues++; + switch (indexingShape) { + case Int32Shape: + case ContiguousShape: + if (butterfly->contiguous()[i]) + numValues++; + break; + + case DoubleShape: { + double value = butterfly->contiguousDouble()[i]; + if (value == value) + numValues++; + break; + } + + default: + CRASH(); + } } return numValues; } +unsigned JSObject::countElements() +{ + switch (structure()->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + return 0; + + case ALL_INT32_INDEXING_TYPES: + return countElements<Int32Shape>(m_butterfly); + + case ALL_DOUBLE_INDEXING_TYPES: + return countElements<DoubleShape>(m_butterfly); + + case ALL_CONTIGUOUS_INDEXING_TYPES: + return countElements<ContiguousShape>(m_butterfly); + + default: + CRASH(); + return 0; + } +} + bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength) { // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map @@ -1839,19 +2356,24 @@ bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength return true; } -void JSObject::ensureContiguousLengthSlow(JSGlobalData& globalData, unsigned length) +void JSObject::ensureLengthSlow(JSGlobalData& globalData, unsigned length) { ASSERT(length < MAX_ARRAY_INDEX); - ASSERT(hasContiguous(structure()->indexingType())); + ASSERT(hasContiguous(structure()->indexingType()) || hasInt32(structure()->indexingType()) || hasDouble(structure()->indexingType()) || hasUndecided(structure()->indexingType())); ASSERT(length > m_butterfly->vectorLength()); unsigned newVectorLength = std::min( length << 1, MAX_STORAGE_VECTOR_LENGTH); + unsigned oldVectorLength = m_butterfly->vectorLength(); m_butterfly = m_butterfly->growArrayRight( globalData, structure(), structure()->outOfLineCapacity(), true, - m_butterfly->vectorLength() * sizeof(EncodedJSValue), + oldVectorLength * sizeof(EncodedJSValue), newVectorLength * sizeof(EncodedJSValue)); + if (hasDouble(structure()->indexingType())) { + for (unsigned i = oldVectorLength; i < newVectorLength; ++i) + m_butterfly->contiguousDouble()[i] = QNaN; + } m_butterfly->setVectorLength(newVectorLength); } @@ -1881,8 +2403,10 @@ bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prope switch (object->structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: return false; + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: { Butterfly* butterfly = object->m_butterfly; if (i >= butterfly->vectorLength()) @@ -1894,6 +2418,17 @@ bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prope return true; } + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = object->m_butterfly; + if (i >= butterfly->vectorLength()) + return false; + double value = butterfly->contiguousDouble()[i]; + if (value != value) + return false; + descriptor.setDescriptor(JSValue(JSValue::EncodeAsDouble, value), 0); + return true; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = object->m_butterfly->arrayStorage(); if (i >= storage->length()) diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h index 82455390f..4f7f4700b 100644 --- a/Source/JavaScriptCore/runtime/JSObject.h +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -39,6 +39,7 @@ #include "Structure.h" #include "JSGlobalData.h" #include "JSString.h" +#include "SlotVisitorInlines.h" #include "SparseArrayValueMap.h" #include <wtf/StdLibExtras.h> @@ -151,30 +152,16 @@ public: unsigned getArrayLength() const { - switch (structure()->indexingType()) { - case ALL_BLANK_INDEXING_TYPES: + if (!hasIndexedProperties(structure()->indexingType())) return 0; - case ALL_CONTIGUOUS_INDEXING_TYPES: - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->publicLength(); - default: - ASSERT_NOT_REACHED(); - return 0; - } + return m_butterfly->publicLength(); } unsigned getVectorLength() { - switch (structure()->indexingType()) { - case ALL_BLANK_INDEXING_TYPES: + if (!hasIndexedProperties(structure()->indexingType())) return 0; - case ALL_CONTIGUOUS_INDEXING_TYPES: - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->vectorLength(); - default: - ASSERT_NOT_REACHED(); - return 0; - } + return m_butterfly->vectorLength(); } JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); @@ -214,9 +201,19 @@ public: { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: return false; + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return i < m_butterfly->vectorLength() && m_butterfly->contiguous()[i]; + case ALL_DOUBLE_INDEXING_TYPES: { + if (i >= m_butterfly->vectorLength()) + return false; + double value = m_butterfly->contiguousDouble()[i]; + if (value != value) + return false; + return true; + } case ALL_ARRAY_STORAGE_INDEXING_TYPES: return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i]; default: @@ -228,8 +225,11 @@ public: JSValue getIndexQuickly(unsigned i) { switch (structure()->indexingType()) { + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return m_butterfly->contiguous()[i].get(); + case ALL_DOUBLE_INDEXING_TYPES: + return JSValue(JSValue::EncodeAsDouble, m_butterfly->contiguousDouble()[i]); case ALL_ARRAY_STORAGE_INDEXING_TYPES: return m_butterfly->arrayStorage()->m_vector[i].get(); default: @@ -242,11 +242,21 @@ public: { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: break; + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: if (i < m_butterfly->publicLength()) return m_butterfly->contiguous()[i].get(); break; + case ALL_DOUBLE_INDEXING_TYPES: { + if (i >= m_butterfly->publicLength()) + break; + double result = m_butterfly->contiguousDouble()[i]; + if (result != result) + break; + return JSValue(JSValue::EncodeAsDouble, result); + } case ALL_ARRAY_STORAGE_INDEXING_TYPES: if (i < m_butterfly->arrayStorage()->vectorLength()) return m_butterfly->arrayStorage()->m_vector[i].get(); @@ -279,7 +289,10 @@ public: { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: return false; + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: case NonArrayWithArrayStorage: case ArrayWithArrayStorage: @@ -298,7 +311,10 @@ public: { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: return false; + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_ARRAY_STORAGE_INDEXING_TYPES: return i < m_butterfly->vectorLength(); @@ -311,6 +327,14 @@ public: void setIndexQuickly(JSGlobalData& globalData, unsigned i, JSValue v) { switch (structure()->indexingType()) { + case ALL_INT32_INDEXING_TYPES: { + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isInt32()) { + convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(globalData, i, v); + return; + } + // Fall through to contiguous case. + } case ALL_CONTIGUOUS_INDEXING_TYPES: { ASSERT(i < m_butterfly->vectorLength()); m_butterfly->contiguous()[i].set(globalData, this, v); @@ -318,6 +342,22 @@ public: m_butterfly->setPublicLength(i + 1); break; } + case ALL_DOUBLE_INDEXING_TYPES: { + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isNumber()) { + convertDoubleToContiguousWhilePerformingSetIndex(globalData, i, v); + return; + } + double value = v.asNumber(); + if (value != value) { + convertDoubleToContiguousWhilePerformingSetIndex(globalData, i, v); + return; + } + m_butterfly->contiguousDouble()[i] = value; + if (i >= m_butterfly->publicLength()) + m_butterfly->setPublicLength(i + 1); + break; + } case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); WriteBarrier<Unknown>& x = storage->m_vector[i]; @@ -338,12 +378,40 @@ public: void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v) { switch (structure()->indexingType()) { + case ALL_UNDECIDED_INDEXING_TYPES: { + setIndexQuicklyToUndecided(globalData, i, v); + break; + } + case ALL_INT32_INDEXING_TYPES: { + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isInt32()) { + convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(globalData, i, v); + break; + } + // Fall through. + } case ALL_CONTIGUOUS_INDEXING_TYPES: { ASSERT(i < m_butterfly->publicLength()); ASSERT(i < m_butterfly->vectorLength()); m_butterfly->contiguous()[i].set(globalData, this, v); break; } + case ALL_DOUBLE_INDEXING_TYPES: { + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isNumber()) { + convertDoubleToContiguousWhilePerformingSetIndex(globalData, i, v); + return; + } + double value = v.asNumber(); + if (value != value) { + convertDoubleToContiguousWhilePerformingSetIndex(globalData, i, v); + return; + } + m_butterfly->contiguousDouble()[i] = value; + break; + } case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); ASSERT(i < storage->length()); @@ -360,6 +428,9 @@ public: { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return false; case ALL_ARRAY_STORAGE_INDEXING_TYPES: @@ -374,6 +445,9 @@ public: { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return false; case ALL_ARRAY_STORAGE_INDEXING_TYPES: @@ -571,6 +645,30 @@ public: // foo->attemptToInterceptPutByIndexOnHole(...); bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow); + // Returns 0 if int32 storage cannot be created - either because + // indexing should be sparse, we're having a bad time, or because + // we already have a more general form of storage (double, + // contiguous, array storage). + WriteBarrier<Unknown>* ensureInt32(JSGlobalData& globalData) + { + if (LIKELY(hasInt32(structure()->indexingType()))) + return m_butterfly->contiguousInt32(); + + return ensureInt32Slow(globalData); + } + + // Returns 0 if double storage cannot be created - either because + // indexing should be sparse, we're having a bad time, or because + // we already have a more general form of storage (contiguous, + // or array storage). + double* ensureDouble(JSGlobalData& globalData) + { + if (LIKELY(hasDouble(structure()->indexingType()))) + return m_butterfly->contiguousDouble(); + + return ensureDoubleSlow(globalData); + } + // Returns 0 if contiguous storage cannot be created - either because // indexing should be sparse or because we're having a bad time. WriteBarrier<Unknown>* ensureContiguous(JSGlobalData& globalData) @@ -593,14 +691,6 @@ public: return ensureArrayStorageSlow(globalData); } - Butterfly* ensureIndexedStorage(JSGlobalData& globalData) - { - if (LIKELY(hasIndexedProperties(structure()->indexingType()))) - return m_butterfly; - - return ensureIndexedStorageSlow(globalData); - } - static size_t offsetOfInlineStorage(); static ptrdiff_t butterflyOffset() @@ -661,19 +751,47 @@ protected: return 0; } } - + + Butterfly* createInitialUndecided(JSGlobalData&, unsigned length); + WriteBarrier<Unknown>* createInitialInt32(JSGlobalData&, unsigned length); + double* createInitialDouble(JSGlobalData&, unsigned length); + WriteBarrier<Unknown>* createInitialContiguous(JSGlobalData&, unsigned length); + + void convertUndecidedForValue(JSGlobalData&, JSValue); + void convertInt32ForValue(JSGlobalData&, JSValue); + ArrayStorage* createArrayStorage(JSGlobalData&, unsigned length, unsigned vectorLength); ArrayStorage* createInitialArrayStorage(JSGlobalData&); - WriteBarrier<Unknown>* createInitialContiguous(JSGlobalData&, unsigned length); + + WriteBarrier<Unknown>* convertUndecidedToInt32(JSGlobalData&); + double* convertUndecidedToDouble(JSGlobalData&); + WriteBarrier<Unknown>* convertUndecidedToContiguous(JSGlobalData&); + ArrayStorage* convertUndecidedToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength); + ArrayStorage* convertUndecidedToArrayStorage(JSGlobalData&, NonPropertyTransition); + ArrayStorage* convertUndecidedToArrayStorage(JSGlobalData&); + + double* convertInt32ToDouble(JSGlobalData&); + WriteBarrier<Unknown>* convertInt32ToContiguous(JSGlobalData&); + ArrayStorage* convertInt32ToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength); + ArrayStorage* convertInt32ToArrayStorage(JSGlobalData&, NonPropertyTransition); + ArrayStorage* convertInt32ToArrayStorage(JSGlobalData&); + + WriteBarrier<Unknown>* convertDoubleToContiguous(JSGlobalData&); + ArrayStorage* convertDoubleToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength); + ArrayStorage* convertDoubleToArrayStorage(JSGlobalData&, NonPropertyTransition); + ArrayStorage* convertDoubleToArrayStorage(JSGlobalData&); + ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength); ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&, NonPropertyTransition); ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&); + ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData&); bool defineOwnNonIndexProperty(ExecState*, PropertyName, PropertyDescriptor&, bool throwException); - void putByIndexBeyondVectorLengthContiguousWithoutAttributes(ExecState*, unsigned propertyName, JSValue); + template<IndexingType indexingShape> + void putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned propertyName, JSValue); void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*); bool increaseVectorLength(JSGlobalData&, unsigned newLength); @@ -687,24 +805,33 @@ protected: // Call this if you want setIndexQuickly to succeed and you're sure that // the array is contiguous. - void ensureContiguousLength(JSGlobalData& globalData, unsigned length) + void ensureLength(JSGlobalData& globalData, unsigned length) { ASSERT(length < MAX_ARRAY_INDEX); - ASSERT(hasContiguous(structure()->indexingType())); + ASSERT(hasContiguous(structure()->indexingType()) || hasInt32(structure()->indexingType()) || hasDouble(structure()->indexingType()) || hasUndecided(structure()->indexingType())); if (m_butterfly->vectorLength() < length) - ensureContiguousLengthSlow(globalData, length); + ensureLengthSlow(globalData, length); if (m_butterfly->publicLength() < length) m_butterfly->setPublicLength(length); } - unsigned countElementsInContiguous(Butterfly*); + template<IndexingType indexingShape> + unsigned countElements(Butterfly*); + // This is relevant to undecided, int32, double, and contiguous. + unsigned countElements(); + + // This strange method returns a pointer to the start of the indexed data + // as if it contained JSValues. But it won't always contain JSValues. + // Make sure you cast this to the appropriate type before using. template<IndexingType indexingType> WriteBarrier<Unknown>* indexingData() { switch (indexingType) { + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return m_butterfly->contiguous(); @@ -720,6 +847,7 @@ protected: WriteBarrier<Unknown>* currentIndexingData() { switch (structure()->indexingType()) { + case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return m_butterfly->contiguous(); @@ -732,10 +860,32 @@ protected: } } + JSValue getHolyIndexQuickly(unsigned i) + { + switch (structure()->indexingType()) { + case ALL_INT32_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + return m_butterfly->contiguous()[i].get(); + case ALL_DOUBLE_INDEXING_TYPES: { + double value = m_butterfly->contiguousDouble()[i]; + if (value == value) + return JSValue(JSValue::EncodeAsDouble, value); + return JSValue(); + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return m_butterfly->arrayStorage()->m_vector[i].get(); + default: + CRASH(); + return JSValue(); + } + } + template<IndexingType indexingType> unsigned relevantLength() { switch (indexingType) { + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return m_butterfly->publicLength(); @@ -753,6 +903,8 @@ protected: unsigned currentRelevantLength() { switch (structure()->indexingType()) { + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: return m_butterfly->publicLength(); @@ -778,6 +930,8 @@ private: void isObject(); void isString(); + Butterfly* createInitialIndexedStorage(JSGlobalData&, unsigned length, size_t elementSize); + ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(JSGlobalData&, ArrayStorage*); template<PutMode> @@ -800,11 +954,18 @@ private: JS_EXPORT_PRIVATE bool getOwnPropertySlotSlow(ExecState*, PropertyName, PropertySlot&); - void ensureContiguousLengthSlow(JSGlobalData&, unsigned length); + ArrayStorage* constructConvertedArrayStorageWithoutCopyingElements(JSGlobalData&, unsigned neededLength); + + JS_EXPORT_PRIVATE void setIndexQuicklyToUndecided(JSGlobalData&, unsigned index, JSValue); + JS_EXPORT_PRIVATE void convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(JSGlobalData&, unsigned index, JSValue); + JS_EXPORT_PRIVATE void convertDoubleToContiguousWhilePerformingSetIndex(JSGlobalData&, unsigned index, JSValue); + + void ensureLengthSlow(JSGlobalData&, unsigned length); + WriteBarrier<Unknown>* ensureInt32Slow(JSGlobalData&); + double* ensureDoubleSlow(JSGlobalData&); WriteBarrier<Unknown>* ensureContiguousSlow(JSGlobalData&); ArrayStorage* ensureArrayStorageSlow(JSGlobalData&); - Butterfly* ensureIndexedStorageSlow(JSGlobalData&); protected: Butterfly* m_butterfly; @@ -1152,6 +1313,8 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p // See comment on setNewProperty call below. if (!specificFunction) slot.setNewProperty(this, offset); + if (attributes & ReadOnly) + structure()->setContainsReadOnlyProperties(); return true; } @@ -1219,6 +1382,8 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p // so leave the slot in an uncachable state. if (!specificFunction) slot.setNewProperty(this, offset); + if (attributes & ReadOnly) + structure->setContainsReadOnlyProperties(); return true; } diff --git a/Source/JavaScriptCore/runtime/JSValue.cpp b/Source/JavaScriptCore/runtime/JSValue.cpp index e7f8cad17..d9253730f 100644 --- a/Source/JavaScriptCore/runtime/JSValue.cpp +++ b/Source/JavaScriptCore/runtime/JSValue.cpp @@ -214,10 +214,18 @@ char* JSValue::description() const snprintf(description, size, "Double: %08x:%08x, %lf", u.asTwoInt32s[1], u.asTwoInt32s[0], asDouble()); #endif } else if (isCell()) { - snprintf( - description, size, "Cell: %p (%p: %s, %s)", - asCell(), asCell()->structure(), asCell()->structure()->classInfo()->className, - indexingTypeToString(asCell()->structure()->indexingTypeIncludingHistory())); + if (asCell()->inherits(&Structure::s_info)) { + Structure* structure = jsCast<Structure*>(asCell()); + snprintf( + description, size, "Structure: %p: %s, %s", + structure, structure->classInfo()->className, + indexingTypeToString(structure->indexingTypeIncludingHistory())); + } else { + snprintf( + description, size, "Cell: %p -> %p (%p: %s, %s)", + asCell(), isObject() ? asObject(*this)->butterfly() : 0, asCell()->structure(), asCell()->structure()->classInfo()->className, + indexingTypeToString(asCell()->structure()->indexingTypeIncludingHistory())); + } } else if (isTrue()) snprintf(description, size, "True"); else if (isFalse()) diff --git a/Source/JavaScriptCore/runtime/JSValueInlineMethods.h b/Source/JavaScriptCore/runtime/JSValueInlines.h index 224982e9e..c5a42f67f 100644 --- a/Source/JavaScriptCore/runtime/JSValueInlineMethods.h +++ b/Source/JavaScriptCore/runtime/JSValueInlines.h @@ -23,8 +23,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef JSValueInlineMethods_h -#define JSValueInlineMethods_h +#ifndef JSValueInlines_h +#define JSValueInlines_h #include "JSValue.h" @@ -493,4 +493,5 @@ namespace JSC { } // namespace JSC -#endif // JSValueInlineMethods_h +#endif // JSValueInlines_h + diff --git a/Source/JavaScriptCore/runtime/LiteralParser.cpp b/Source/JavaScriptCore/runtime/LiteralParser.cpp index cd854417b..bf27327bf 100644 --- a/Source/JavaScriptCore/runtime/LiteralParser.cpp +++ b/Source/JavaScriptCore/runtime/LiteralParser.cpp @@ -27,8 +27,8 @@ #include "config.h" #include "LiteralParser.h" -#include "ButterflyInlineMethods.h" -#include "CopiedSpaceInlineMethods.h" +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" #include "JSArray.h" #include "JSString.h" #include "Lexer.h" @@ -548,7 +548,7 @@ JSValue LiteralParser<CharType>::parse(ParserState initialState) switch(state) { startParseArray: case StartParseArray: { - JSArray* array = constructEmptyArray(m_exec); + JSArray* array = constructEmptyArray(m_exec, 0); objectStack.append(array); // fallthrough } diff --git a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp index 7df047d28..7e74a914b 100644 --- a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp +++ b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -21,8 +21,8 @@ #include "config.h" #include "ObjectConstructor.h" -#include "ButterflyInlineMethods.h" -#include "CopiedSpaceInlineMethods.h" +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" #include "Error.h" #include "ExceptionHelpers.h" #include "JSFunction.h" @@ -182,7 +182,7 @@ EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exe return throwVMError(exec, createTypeError(exec, ASCIILiteral("Requested property names of a value that is not an object."))); PropertyNameArray properties(exec); asObject(exec->argument(0))->methodTable()->getOwnPropertyNames(asObject(exec->argument(0)), exec, properties, IncludeDontEnumProperties); - JSArray* names = constructEmptyArray(exec); + JSArray* names = constructEmptyArray(exec, 0); size_t numProperties = properties.size(); for (size_t i = 0; i < numProperties; i++) names->push(exec, jsOwnedString(exec, properties[i].string())); @@ -196,7 +196,7 @@ EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec) return throwVMError(exec, createTypeError(exec, ASCIILiteral("Requested keys of a value that is not an object."))); PropertyNameArray properties(exec); asObject(exec->argument(0))->methodTable()->getOwnPropertyNames(asObject(exec->argument(0)), exec, properties, ExcludeDontEnumProperties); - JSArray* keys = constructEmptyArray(exec); + JSArray* keys = constructEmptyArray(exec, 0); size_t numProperties = properties.size(); for (size_t i = 0; i < numProperties; i++) keys->push(exec, jsOwnedString(exec, properties[i].string())); diff --git a/Source/JavaScriptCore/runtime/Operations.h b/Source/JavaScriptCore/runtime/Operations.h index 01df7e98c..7301bf6ec 100644 --- a/Source/JavaScriptCore/runtime/Operations.h +++ b/Source/JavaScriptCore/runtime/Operations.h @@ -26,7 +26,7 @@ #include "Interpreter.h" #include "JSProxy.h" #include "JSString.h" -#include "JSValueInlineMethods.h" +#include "JSValueInlines.h" namespace JSC { @@ -86,6 +86,7 @@ namespace JSC { if (ropeBuilder.length() < oldLength) // True for overflow return throwOutOfMemoryError(exec); + oldLength = ropeBuilder.length(); } return ropeBuilder.release(); @@ -105,6 +106,7 @@ namespace JSC { if (ropeBuilder.length() < oldLength) // True for overflow return throwOutOfMemoryError(exec); + oldLength = ropeBuilder.length(); } return ropeBuilder.release(); @@ -356,6 +358,23 @@ namespace JSC { } } + inline bool isPrototypeChainNormalized(JSGlobalObject* globalObject, Structure* structure) + { + for (;;) { + if (structure->typeInfo().type() == ProxyType) + return false; + + JSValue v = structure->prototypeForLookup(globalObject); + if (v.isNull()) + return true; + + structure = v.asCell()->structure(); + + if (structure->isDictionary()) + return false; + } + } + } // namespace JSC #endif // Operations_h diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h index d6d8c66c8..99a5f85a2 100644 --- a/Source/JavaScriptCore/runtime/Options.h +++ b/Source/JavaScriptCore/runtime/Options.h @@ -66,6 +66,8 @@ namespace JSC { v(bool, useDFGJIT, true) \ v(bool, useRegExpJIT, true) \ \ + v(bool, forceDFGCodeBlockLiveness, false) \ + \ /* showDisassembly implies showDFGDisassembly. */ \ v(bool, showDisassembly, false) \ v(bool, showDFGDisassembly, false) \ diff --git a/Source/JavaScriptCore/runtime/RegExp.cpp b/Source/JavaScriptCore/runtime/RegExp.cpp index 7757274f1..3229f5207 100644 --- a/Source/JavaScriptCore/runtime/RegExp.cpp +++ b/Source/JavaScriptCore/runtime/RegExp.cpp @@ -520,24 +520,24 @@ void RegExp::matchCompareWithInterpreter(const String& s, int startOffset, int* differences++; if (differences) { - dataLog("RegExp Discrepency for /%s/\n string input ", pattern().utf8().data()); + dataLogF("RegExp Discrepency for /%s/\n string input ", pattern().utf8().data()); unsigned segmentLen = s.length() - static_cast<unsigned>(startOffset); - dataLog((segmentLen < 150) ? "\"%s\"\n" : "\"%148s...\"\n", s.utf8().data() + startOffset); + dataLogF((segmentLen < 150) ? "\"%s\"\n" : "\"%148s...\"\n", s.utf8().data() + startOffset); if (jitResult != interpreterResult) { - dataLog(" JIT result = %d, blah interpreted result = %d\n", jitResult, interpreterResult); + dataLogF(" JIT result = %d, blah interpreted result = %d\n", jitResult, interpreterResult); differences--; } else { - dataLog(" Correct result = %d\n", jitResult); + dataLogF(" Correct result = %d\n", jitResult); } if (differences) { for (unsigned j = 2, i = 0; i < m_numSubpatterns; j +=2, i++) { if (offsetVector[j] != interpreterOffsetVector[j]) - dataLog(" JIT offset[%d] = %d, interpreted offset[%d] = %d\n", j, offsetVector[j], j, interpreterOffsetVector[j]); + dataLogF(" JIT offset[%d] = %d, interpreted offset[%d] = %d\n", j, offsetVector[j], j, interpreterOffsetVector[j]); if ((offsetVector[j] >= 0) && (offsetVector[j+1] != interpreterOffsetVector[j+1])) - dataLog(" JIT offset[%d] = %d, interpreted offset[%d] = %d\n", j+1, offsetVector[j+1], j+1, interpreterOffsetVector[j+1]); + dataLogF(" JIT offset[%d] = %d, interpreted offset[%d] = %d\n", j+1, offsetVector[j+1], j+1, interpreterOffsetVector[j+1]); } } } diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp index ce9c2d2db..19f3b81ad 100644 --- a/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp +++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp @@ -26,7 +26,7 @@ #include "config.h" #include "RegExpMatchesArray.h" -#include "ButterflyInlineMethods.h" +#include "ButterflyInlines.h" namespace JSC { diff --git a/Source/JavaScriptCore/runtime/RegExpObject.cpp b/Source/JavaScriptCore/runtime/RegExpObject.cpp index 35de40912..00dd1ed74 100644 --- a/Source/JavaScriptCore/runtime/RegExpObject.cpp +++ b/Source/JavaScriptCore/runtime/RegExpObject.cpp @@ -21,8 +21,8 @@ #include "config.h" #include "RegExpObject.h" -#include "ButterflyInlineMethods.h" -#include "CopiedSpaceInlineMethods.h" +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" #include "Error.h" #include "ExceptionHelpers.h" #include "JSArray.h" diff --git a/Source/JavaScriptCore/runtime/SamplingCounter.cpp b/Source/JavaScriptCore/runtime/SamplingCounter.cpp index abed763ca..9826b88e4 100644 --- a/Source/JavaScriptCore/runtime/SamplingCounter.cpp +++ b/Source/JavaScriptCore/runtime/SamplingCounter.cpp @@ -35,10 +35,10 @@ void AbstractSamplingCounter::dump() { #if ENABLE(SAMPLING_COUNTERS) if (s_abstractSamplingCounterChain != &s_abstractSamplingCounterChainEnd) { - dataLog("\nSampling Counter Values:\n"); + dataLogF("\nSampling Counter Values:\n"); for (AbstractSamplingCounter* currCounter = s_abstractSamplingCounterChain; (currCounter != &s_abstractSamplingCounterChainEnd); currCounter = currCounter->m_next) - dataLog("\t%s\t: %lld\n", currCounter->m_name, currCounter->m_counter); - dataLog("\n\n"); + dataLogF("\t%s\t: %lld\n", currCounter->m_name, currCounter->m_counter); + dataLogF("\n\n"); } s_completed = true; #endif diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp index 5aafe8bb3..93009d806 100644 --- a/Source/JavaScriptCore/runtime/StringPrototype.cpp +++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp @@ -22,9 +22,9 @@ #include "config.h" #include "StringPrototype.h" -#include "ButterflyInlineMethods.h" +#include "ButterflyInlines.h" #include "CachedCall.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "Error.h" #include "Executable.h" #include "JSGlobalObjectFunctions.h" @@ -870,7 +870,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec) return JSValue::encode(jsNull()); } - return JSValue::encode(constructArray(exec, list)); + return JSValue::encode(constructArray(exec, static_cast<ArrayAllocationProfile*>(0), list)); } EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec) @@ -973,7 +973,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) // 3. Let A be a new array created as if by the expression new Array() // where Array is the standard built-in constructor with that name. - JSArray* result = constructEmptyArray(exec); + JSArray* result = constructEmptyArray(exec, 0); // 4. Let lengthA be 0. unsigned resultLength = 0; @@ -1388,7 +1388,10 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec) return throwVMTypeError(exec); String s = thisValue.toString(exec)->value(exec); JSValue a0 = exec->argument(0); - return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", a0.toString(exec)->value(exec), "\">", s, "</font>")); + String color = a0.toWTFString(exec); + color.replaceWithLiteral('"', """); + + return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", color, "\">", s, "</font>")); } EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec) @@ -1433,7 +1436,10 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec) return JSValue::encode(jsNontrivialString(exec, impl)); } - return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", a0.toString(exec)->value(exec), "\">", s, "</font>")); + String fontSize = a0.toWTFString(exec); + fontSize.replaceWithLiteral('"', """); + + return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", fontSize, "\">", s, "</font>")); } EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec) @@ -1443,7 +1449,10 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec) return throwVMTypeError(exec); String s = thisValue.toString(exec)->value(exec); JSValue a0 = exec->argument(0); - return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", a0.toString(exec)->value(exec), "\">", s, "</a>")); + String anchor = a0.toWTFString(exec); + anchor.replaceWithLiteral('"', """); + + return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", anchor, "\">", s, "</a>")); } EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec) @@ -1453,7 +1462,8 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec) return throwVMTypeError(exec); String s = thisValue.toString(exec)->value(exec); JSValue a0 = exec->argument(0); - String linkText = a0.toString(exec)->value(exec); + String linkText = a0.toWTFString(exec); + linkText.replaceWithLiteral('"', """); unsigned linkTextSize = linkText.length(); unsigned stringSize = s.length(); diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp index e733c7e23..9ffe3b060 100644 --- a/Source/JavaScriptCore/runtime/Structure.cpp +++ b/Source/JavaScriptCore/runtime/Structure.cpp @@ -135,17 +135,17 @@ void Structure::dumpStatistics() } } - dataLog("Number of live Structures: %d\n", liveStructureSet.size()); - dataLog("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot); - dataLog("Number of Structures that are leaf nodes: %d\n", numberLeaf); - dataLog("Number of Structures that singletons: %d\n", numberSingletons); - dataLog("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps); - - dataLog("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure))); - dataLog("Size of sum of all property maps: %d\n", totalPropertyMapsSize); - dataLog("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size())); + dataLogF("Number of live Structures: %d\n", liveStructureSet.size()); + dataLogF("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot); + dataLogF("Number of Structures that are leaf nodes: %d\n", numberLeaf); + dataLogF("Number of Structures that singletons: %d\n", numberSingletons); + dataLogF("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps); + + dataLogF("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure))); + dataLogF("Size of sum of all property maps: %d\n", totalPropertyMapsSize); + dataLogF("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size())); #else - dataLog("Dumping Structure statistics is not enabled.\n"); + dataLogF("Dumping Structure statistics is not enabled.\n"); #endif } @@ -543,12 +543,13 @@ Structure* Structure::nonPropertyTransition(JSGlobalData& globalData, Structure* unsigned attributes = toAttributes(transitionKind); IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind); - JSGlobalObject* globalObject = structure->globalObject(); - if (structure == globalObject->arrayStructure()) { - Structure* transition = globalObject->arrayStructureWithArrayStorage(); - if (transition->indexingTypeIncludingHistory() == indexingType) { - structure->notifyTransitionFromThisStructure(); - return transition; + if (JSGlobalObject* globalObject = structure->m_globalObject.get()) { + if (globalObject->isOriginalArrayStructure(structure)) { + Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType); + if (result->indexingTypeIncludingHistory() == indexingType) { + structure->notifyTransitionFromThisStructure(); + return result; + } } } @@ -694,11 +695,11 @@ static PropertyMapStatisticsExitLogger logger; PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() { - dataLog("\nJSC::PropertyMap statistics\n\n"); - dataLog("%d probes\n", numProbes); - dataLog("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes); - dataLog("%d rehashes\n", numRehashes); - dataLog("%d removes\n", numRemoves); + dataLogF("\nJSC::PropertyMap statistics\n\n"); + dataLogF("%d probes\n", numProbes); + dataLogF("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes); + dataLogF("%d rehashes\n", numRehashes); + dataLogF("%d removes\n", numRemoves); } #endif @@ -861,6 +862,32 @@ void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor) visitor.append(&thisObject->m_objectToStringValue); } +bool Structure::prototypeChainMayInterceptStoreTo(JSGlobalData& globalData, PropertyName propertyName) +{ + unsigned i = propertyName.asIndex(); + if (i != PropertyName::NotAnIndex) + return anyObjectInChainMayInterceptIndexedAccesses(); + + for (Structure* current = this; ;) { + JSValue prototype = current->storedPrototype(); + if (prototype.isNull()) + return false; + + current = prototype.asCell()->structure(); + + unsigned attributes; + JSCell* specificValue; + PropertyOffset offset = current->get(globalData, propertyName, attributes, specificValue); + if (!JSC::isValidOffset(offset)) + continue; + + if (attributes & (ReadOnly | Accessor)) + return true; + + return false; + } +} + #if DO_PROPERTYMAP_CONSTENCY_CHECK void PropertyTable::checkConsistency() diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h index 2b25803a6..6e4402c52 100644 --- a/Source/JavaScriptCore/runtime/Structure.h +++ b/Source/JavaScriptCore/runtime/Structure.h @@ -166,9 +166,15 @@ namespace JSC { JSValue prototypeForLookup(ExecState*) const; JSValue prototypeForLookup(JSGlobalObject*) const; JSValue prototypeForLookup(CodeBlock*) const; + StructureChain* prototypeChain(JSGlobalData&, JSGlobalObject*) const; StructureChain* prototypeChain(ExecState*) const; static void visitChildren(JSCell*, SlotVisitor&); - + + // Will just the prototype chain intercept this property access? + bool prototypeChainMayInterceptStoreTo(JSGlobalData&, PropertyName); + + bool transitionDidInvolveSpecificValue() const { return !!m_specificValueInPrevious; } + Structure* previousID() const { ASSERT(structure()->classInfo() == &s_info); @@ -397,6 +403,7 @@ namespace JSC { return numberOfSlotsForLastOffset(m_offset, m_typeInfo.type()); } + bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain) const; bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const; void pin(); diff --git a/Source/JavaScriptCore/runtime/StructureTransitionTable.h b/Source/JavaScriptCore/runtime/StructureTransitionTable.h index 3ab7b2014..5291ed540 100644 --- a/Source/JavaScriptCore/runtime/StructureTransitionTable.h +++ b/Source/JavaScriptCore/runtime/StructureTransitionTable.h @@ -43,6 +43,9 @@ static const unsigned FirstInternalAttribute = 1 << 6; // Use for transitions th // Support for attributes used to indicate transitions not related to properties. // If any of these are used, the string portion of the key should be 0. enum NonPropertyTransition { + AllocateUndecided, + AllocateInt32, + AllocateDouble, AllocateContiguous, AllocateArrayStorage, AllocateSlowPutArrayStorage, @@ -58,14 +61,23 @@ inline unsigned toAttributes(NonPropertyTransition transition) inline IndexingType newIndexingType(IndexingType oldType, NonPropertyTransition transition) { switch (transition) { - case AllocateContiguous: + case AllocateUndecided: ASSERT(!hasIndexedProperties(oldType)); - return oldType | ContiguousShape; + return oldType | UndecidedShape; + case AllocateInt32: + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType)); + return (oldType & ~IndexingShapeMask) | Int32Shape; + case AllocateDouble: + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType)); + return (oldType & ~IndexingShapeMask) | DoubleShape; + case AllocateContiguous: + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType)); + return (oldType & ~IndexingShapeMask) | ContiguousShape; case AllocateArrayStorage: - ASSERT(!hasIndexedProperties(oldType) || hasContiguous(oldType)); + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType)); return (oldType & ~IndexingShapeMask) | ArrayStorageShape; case AllocateSlowPutArrayStorage: - ASSERT(!hasIndexedProperties(oldType) || hasContiguous(oldType)); + ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType) || hasContiguous(oldType)); return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape; case SwitchToSlowPutArrayStorage: ASSERT(hasFastArrayStorage(oldType)); diff --git a/Source/JavaScriptCore/tools/CodeProfile.cpp b/Source/JavaScriptCore/tools/CodeProfile.cpp index 0020c7285..b3c4ff448 100644 --- a/Source/JavaScriptCore/tools/CodeProfile.cpp +++ b/Source/JavaScriptCore/tools/CodeProfile.cpp @@ -143,7 +143,7 @@ void CodeProfile::sample(void* pc, void** framePointer) void CodeProfile::report() { - dataLog("<CodeProfiling %s:%d>\n", m_file.data(), m_lineNo); + dataLogF("<CodeProfiling %s:%d>\n", m_file.data(), m_lineNo); // How many frames of C-code to print - 0, if not verbose, 1 if verbose, up to 1024 if very verbose. unsigned recursionLimit = CodeProfiling::beVeryVerbose() ? 1024 : CodeProfiling::beVerbose(); @@ -180,13 +180,13 @@ void CodeProfile::report() } // Output the profile tree. - dataLog("Total samples: %lld\n", static_cast<long long>(profile.childCount())); + dataLogF("Total samples: %lld\n", static_cast<long long>(profile.childCount())); profile.dump(); for (size_t i = 0 ; i < m_children.size(); ++i) m_children[i]->report(); - dataLog("</CodeProfiling %s:%d>\n", m_file.data(), m_lineNo); + dataLogF("</CodeProfiling %s:%d>\n", m_file.data(), m_lineNo); } } diff --git a/Source/JavaScriptCore/tools/ProfileTreeNode.h b/Source/JavaScriptCore/tools/ProfileTreeNode.h index 7b5a95ec4..4daa7df4c 100644 --- a/Source/JavaScriptCore/tools/ProfileTreeNode.h +++ b/Source/JavaScriptCore/tools/ProfileTreeNode.h @@ -95,8 +95,8 @@ private: // Print the number of samples, the name of this node, and the number of samples that are stack-top // in this node (samples directly within this node, excluding samples in children. for (unsigned i = 0; i < indent; ++i) - dataLog(" "); - dataLog("% 8lld: %s (%lld stack top)\n", + dataLogF(" "); + dataLogF("% 8lld: %s (%lld stack top)\n", static_cast<long long>(entry->value.count()), entry->key.utf8().data(), static_cast<long long>(entry->value.count() - entry->value.childCount())); diff --git a/Source/JavaScriptCore/yarr/YarrInterpreter.cpp b/Source/JavaScriptCore/yarr/YarrInterpreter.cpp index 5c2a287c8..31603f6d3 100644 --- a/Source/JavaScriptCore/yarr/YarrInterpreter.cpp +++ b/Source/JavaScriptCore/yarr/YarrInterpreter.cpp @@ -1650,10 +1650,10 @@ public: #ifndef NDEBUG void dumpDisjunction(ByteDisjunction* disjunction) { - dataLog("ByteDisjunction(%p):\n\t", disjunction); + dataLogF("ByteDisjunction(%p):\n\t", disjunction); for (unsigned i = 0; i < disjunction->terms.size(); ++i) - dataLog("{ %d } ", disjunction->terms[i].type); - dataLog("\n"); + dataLogF("{ %d } ", disjunction->terms[i].type); + dataLogF("\n"); } #endif |