From 254d43bc9820a3fe7d5d9a31b98dade575894b1e Mon Sep 17 00:00:00 2001 From: "fpizlo@apple.com" Date: Mon, 5 Mar 2012 06:52:44 +0000 Subject: [PATCH] JIT heuristics should be hyperbolic https://bugs.webkit.org/show_bug.cgi?id=80055 Source/JavaScriptCore: Reviewed by Oliver Hunt. Added tracking of the amount of executable memory typically used for a bytecode instruction. Modified the execution counter scheme to use this, and the amount of free memory, to determine how long to wait before invoking the JIT. The result is that even if we bomb the VM with more code than can fit in our executable memory pool, we still keep running and almost never run out of executable memory - which ensures that if we have to JIT something critical, then we'll likely have enough memory to do so. This also does not regress performance on the three main benchmarks. * CMakeLists.txt: * GNUmakefile.list.am: * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: * JavaScriptCore.xcodeproj/project.pbxproj: * Target.pri: * bytecode/CodeBlock.cpp: (JSC::CodeBlock::predictedMachineCodeSize): (JSC): (JSC::CodeBlock::usesOpcode): * bytecode/CodeBlock.h: (CodeBlock): (JSC::CodeBlock::checkIfJITThresholdReached): (JSC::CodeBlock::dontJITAnytimeSoon): (JSC::CodeBlock::jitAfterWarmUp): (JSC::CodeBlock::jitSoon): (JSC::CodeBlock::llintExecuteCounter): (JSC::CodeBlock::counterValueForOptimizeAfterWarmUp): (JSC::CodeBlock::counterValueForOptimizeAfterLongWarmUp): (JSC::CodeBlock::addressOfJITExecuteCounter): (JSC::CodeBlock::offsetOfJITExecuteCounter): (JSC::CodeBlock::offsetOfJITExecutionActiveThreshold): (JSC::CodeBlock::offsetOfJITExecutionTotalCount): (JSC::CodeBlock::jitExecuteCounter): (JSC::CodeBlock::checkIfOptimizationThresholdReached): (JSC::CodeBlock::optimizeNextInvocation): (JSC::CodeBlock::dontOptimizeAnytimeSoon): (JSC::CodeBlock::optimizeAfterWarmUp): (JSC::CodeBlock::optimizeAfterLongWarmUp): (JSC::CodeBlock::optimizeSoon): * bytecode/ExecutionCounter.cpp: Added. (JSC): (JSC::ExecutionCounter::ExecutionCounter): (JSC::ExecutionCounter::checkIfThresholdCrossedAndSet): (JSC::ExecutionCounter::setNewThreshold): (JSC::ExecutionCounter::deferIndefinitely): (JSC::ExecutionCounter::applyMemoryUsageHeuristics): (JSC::ExecutionCounter::applyMemoryUsageHeuristicsAndConvertToInt): (JSC::ExecutionCounter::hasCrossedThreshold): (JSC::ExecutionCounter::setThreshold): (JSC::ExecutionCounter::reset): * bytecode/ExecutionCounter.h: Added. (JSC): (ExecutionCounter): (JSC::ExecutionCounter::formattedTotalCount): * dfg/DFGOSRExitCompiler32_64.cpp: (JSC::DFG::OSRExitCompiler::compileExit): * dfg/DFGOSRExitCompiler64.cpp: (JSC::DFG::OSRExitCompiler::compileExit): * jit/ExecutableAllocator.cpp: (JSC::DemandExecutableAllocator::allocateNewSpace): (JSC::ExecutableAllocator::underMemoryPressure): (JSC): (JSC::ExecutableAllocator::memoryPressureMultiplier): * jit/ExecutableAllocator.h: * jit/ExecutableAllocatorFixedVMPool.cpp: (JSC::ExecutableAllocator::memoryPressureMultiplier): (JSC): * jit/JIT.cpp: (JSC::JIT::privateCompile): * jit/JITStubs.cpp: (JSC::DEFINE_STUB_FUNCTION): * llint/LLIntSlowPaths.cpp: (JSC::LLInt::jitCompileAndSetHeuristics): * llint/LowLevelInterpreter32_64.asm: * runtime/JSGlobalData.h: (JSGlobalData): * runtime/Options.cpp: (Options): (JSC::Options::initializeOptions): * runtime/Options.h: (Options): * wtf/SimpleStats.h: Added. (WTF): (SimpleStats): (WTF::SimpleStats::SimpleStats): (WTF::SimpleStats::add): (WTF::SimpleStats::operator!): (WTF::SimpleStats::count): (WTF::SimpleStats::sum): (WTF::SimpleStats::sumOfSquares): (WTF::SimpleStats::mean): (WTF::SimpleStats::variance): (WTF::SimpleStats::standardDeviation): Source/WebCore: Reviewed by Oliver Hunt. No new tests, since there's no new functionality. * ForwardingHeaders/wtf/SimpleStats.h: Added. git-svn-id: http://svn.webkit.org/repository/webkit/trunk@109705 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- Source/JavaScriptCore/CMakeLists.txt | 1 + Source/JavaScriptCore/ChangeLog | 102 +++++++++++++ Source/JavaScriptCore/GNUmakefile.list.am | 3 + .../JavaScriptCore/JavaScriptCore.vcproj | 4 + .../JavaScriptCore.xcodeproj/project.pbxproj | 12 ++ Source/JavaScriptCore/Target.pri | 1 + Source/JavaScriptCore/bytecode/CodeBlock.cpp | 34 ++++- Source/JavaScriptCore/bytecode/CodeBlock.h | 55 ++++--- .../JavaScriptCore/bytecode/ExecutionCounter.cpp | 159 +++++++++++++++++++++ Source/JavaScriptCore/bytecode/ExecutionCounter.h | 83 +++++++++++ .../JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp | 12 +- Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp | 12 +- Source/JavaScriptCore/jit/ExecutableAllocator.cpp | 29 ++++ Source/JavaScriptCore/jit/ExecutableAllocator.h | 2 + .../jit/ExecutableAllocatorFixedVMPool.cpp | 11 ++ Source/JavaScriptCore/jit/JIT.cpp | 4 + Source/JavaScriptCore/jit/JITStubs.cpp | 9 +- Source/JavaScriptCore/llint/LLIntSlowPaths.cpp | 7 + .../llint/LowLevelInterpreter32_64.asm | 4 +- Source/JavaScriptCore/runtime/JSGlobalData.h | 2 + Source/JavaScriptCore/runtime/Options.cpp | 40 +++--- Source/JavaScriptCore/runtime/Options.h | 16 +-- Source/JavaScriptCore/wtf/SimpleStats.h | 107 ++++++++++++++ Source/WebCore/ChangeLog | 12 ++ Source/WebCore/ForwardingHeaders/wtf/SimpleStats.h | 4 + 25 files changed, 665 insertions(+), 60 deletions(-) create mode 100644 Source/JavaScriptCore/bytecode/ExecutionCounter.cpp create mode 100644 Source/JavaScriptCore/bytecode/ExecutionCounter.h create mode 100644 Source/JavaScriptCore/wtf/SimpleStats.h create mode 100644 Source/WebCore/ForwardingHeaders/wtf/SimpleStats.h diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt index bad6e10..0f704ed 100644 --- a/Source/JavaScriptCore/CMakeLists.txt +++ b/Source/JavaScriptCore/CMakeLists.txt @@ -40,6 +40,7 @@ SET(JavaScriptCore_SOURCES bytecode/CallLinkStatus.cpp bytecode/CodeBlock.cpp bytecode/DFGExitProfile.cpp + bytecode/ExecutionCounter.cpp bytecode/GetByIdStatus.cpp bytecode/JumpTable.cpp bytecode/LazyOperandValueProfile.cpp diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog index afd64b2..98d6549 100644 --- a/Source/JavaScriptCore/ChangeLog +++ b/Source/JavaScriptCore/ChangeLog @@ -1,3 +1,105 @@ +2012-03-04 Filip Pizlo + + JIT heuristics should be hyperbolic + https://bugs.webkit.org/show_bug.cgi?id=80055 + + + Reviewed by Oliver Hunt. + + Added tracking of the amount of executable memory typically used for a bytecode + instruction. Modified the execution counter scheme to use this, and the amount + of free memory, to determine how long to wait before invoking the JIT. + + The result is that even if we bomb the VM with more code than can fit in our + executable memory pool, we still keep running and almost never run out of + executable memory - which ensures that if we have to JIT something critical, then + we'll likely have enough memory to do so. This also does not regress performance + on the three main benchmarks. + + * CMakeLists.txt: + * GNUmakefile.list.am: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: + * JavaScriptCore.xcodeproj/project.pbxproj: + * Target.pri: + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::predictedMachineCodeSize): + (JSC): + (JSC::CodeBlock::usesOpcode): + * bytecode/CodeBlock.h: + (CodeBlock): + (JSC::CodeBlock::checkIfJITThresholdReached): + (JSC::CodeBlock::dontJITAnytimeSoon): + (JSC::CodeBlock::jitAfterWarmUp): + (JSC::CodeBlock::jitSoon): + (JSC::CodeBlock::llintExecuteCounter): + (JSC::CodeBlock::counterValueForOptimizeAfterWarmUp): + (JSC::CodeBlock::counterValueForOptimizeAfterLongWarmUp): + (JSC::CodeBlock::addressOfJITExecuteCounter): + (JSC::CodeBlock::offsetOfJITExecuteCounter): + (JSC::CodeBlock::offsetOfJITExecutionActiveThreshold): + (JSC::CodeBlock::offsetOfJITExecutionTotalCount): + (JSC::CodeBlock::jitExecuteCounter): + (JSC::CodeBlock::checkIfOptimizationThresholdReached): + (JSC::CodeBlock::optimizeNextInvocation): + (JSC::CodeBlock::dontOptimizeAnytimeSoon): + (JSC::CodeBlock::optimizeAfterWarmUp): + (JSC::CodeBlock::optimizeAfterLongWarmUp): + (JSC::CodeBlock::optimizeSoon): + * bytecode/ExecutionCounter.cpp: Added. + (JSC): + (JSC::ExecutionCounter::ExecutionCounter): + (JSC::ExecutionCounter::checkIfThresholdCrossedAndSet): + (JSC::ExecutionCounter::setNewThreshold): + (JSC::ExecutionCounter::deferIndefinitely): + (JSC::ExecutionCounter::applyMemoryUsageHeuristics): + (JSC::ExecutionCounter::applyMemoryUsageHeuristicsAndConvertToInt): + (JSC::ExecutionCounter::hasCrossedThreshold): + (JSC::ExecutionCounter::setThreshold): + (JSC::ExecutionCounter::reset): + * bytecode/ExecutionCounter.h: Added. + (JSC): + (ExecutionCounter): + (JSC::ExecutionCounter::formattedTotalCount): + * dfg/DFGOSRExitCompiler32_64.cpp: + (JSC::DFG::OSRExitCompiler::compileExit): + * dfg/DFGOSRExitCompiler64.cpp: + (JSC::DFG::OSRExitCompiler::compileExit): + * jit/ExecutableAllocator.cpp: + (JSC::DemandExecutableAllocator::allocateNewSpace): + (JSC::ExecutableAllocator::underMemoryPressure): + (JSC): + (JSC::ExecutableAllocator::memoryPressureMultiplier): + * jit/ExecutableAllocator.h: + * jit/ExecutableAllocatorFixedVMPool.cpp: + (JSC::ExecutableAllocator::memoryPressureMultiplier): + (JSC): + * jit/JIT.cpp: + (JSC::JIT::privateCompile): + * jit/JITStubs.cpp: + (JSC::DEFINE_STUB_FUNCTION): + * llint/LLIntSlowPaths.cpp: + (JSC::LLInt::jitCompileAndSetHeuristics): + * llint/LowLevelInterpreter32_64.asm: + * runtime/JSGlobalData.h: + (JSGlobalData): + * runtime/Options.cpp: + (Options): + (JSC::Options::initializeOptions): + * runtime/Options.h: + (Options): + * wtf/SimpleStats.h: Added. + (WTF): + (SimpleStats): + (WTF::SimpleStats::SimpleStats): + (WTF::SimpleStats::add): + (WTF::SimpleStats::operator!): + (WTF::SimpleStats::count): + (WTF::SimpleStats::sum): + (WTF::SimpleStats::sumOfSquares): + (WTF::SimpleStats::mean): + (WTF::SimpleStats::variance): + (WTF::SimpleStats::standardDeviation): + 2012-03-04 Raphael Kubo da Costa [CMake] Libraries are installed to /usr/lib and not /usr/lib64 on x86_64 diff --git a/Source/JavaScriptCore/GNUmakefile.list.am b/Source/JavaScriptCore/GNUmakefile.list.am index f2e51fc..ebd82e7 100644 --- a/Source/JavaScriptCore/GNUmakefile.list.am +++ b/Source/JavaScriptCore/GNUmakefile.list.am @@ -95,6 +95,8 @@ javascriptcore_sources += \ Source/JavaScriptCore/bytecode/DFGExitProfile.cpp \ Source/JavaScriptCore/bytecode/DFGExitProfile.h \ Source/JavaScriptCore/bytecode/EvalCodeCache.h \ + Source/JavaScriptCore/bytecode/ExecutionCounter.cpp \ + Source/JavaScriptCore/bytecode/ExecutionCounter.h \ Source/JavaScriptCore/bytecode/ExpressionRangeInfo.h \ Source/JavaScriptCore/bytecode/GetByIdStatus.cpp \ Source/JavaScriptCore/bytecode/GetByIdStatus.h \ @@ -716,6 +718,7 @@ javascriptcore_sources += \ Source/JavaScriptCore/wtf/SentinelLinkedList.h \ Source/JavaScriptCore/wtf/SHA1.cpp \ Source/JavaScriptCore/wtf/SHA1.h \ + Source/JavaScriptCore/wtf/SimpleStats.h \ Source/JavaScriptCore/wtf/SinglyLinkedList.h \ Source/JavaScriptCore/wtf/Spectrum.h \ Source/JavaScriptCore/wtf/StackBounds.cpp \ diff --git a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj index b9e0f31..8599ded 100644 --- a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj +++ b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj @@ -1478,6 +1478,10 @@ Name="bytecode" > + + diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj index 73514da..fc3d45f 100644 --- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj +++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj @@ -106,6 +106,9 @@ 0F4680D514BBD24B00BFE272 /* HostCallReturnValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4680D114BBC5F800BFE272 /* HostCallReturnValue.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F55F0F414D1063900AC7649 /* AbstractPC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F55F0F114D1063600AC7649 /* AbstractPC.cpp */; }; 0F55F0F514D1063C00AC7649 /* AbstractPC.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F55F0F214D1063600AC7649 /* AbstractPC.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 0F56A1D315000F35002992B1 /* ExecutionCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F56A1D115000F31002992B1 /* ExecutionCounter.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 0F56A1D515001CF4002992B1 /* ExecutionCounter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F56A1D415001CF2002992B1 /* ExecutionCounter.cpp */; }; + 0F56A1D7150028BB002992B1 /* SimpleStats.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F56A1D6150028B9002992B1 /* SimpleStats.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F5F08CF146C7633000472A9 /* UnconditionalFinalizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5F08CE146C762F000472A9 /* UnconditionalFinalizer.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F620174143FCD330068B77C /* DFGVariableAccessData.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F620172143FCD2F0068B77C /* DFGVariableAccessData.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F620175143FCD370068B77C /* DFGOperands.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F620171143FCD2F0068B77C /* DFGOperands.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -974,6 +977,9 @@ 0F4680D114BBC5F800BFE272 /* HostCallReturnValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HostCallReturnValue.h; sourceTree = ""; }; 0F55F0F114D1063600AC7649 /* AbstractPC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AbstractPC.cpp; sourceTree = ""; }; 0F55F0F214D1063600AC7649 /* AbstractPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AbstractPC.h; sourceTree = ""; }; + 0F56A1D115000F31002992B1 /* ExecutionCounter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExecutionCounter.h; sourceTree = ""; }; + 0F56A1D415001CF2002992B1 /* ExecutionCounter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExecutionCounter.cpp; sourceTree = ""; }; + 0F56A1D6150028B9002992B1 /* SimpleStats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleStats.h; sourceTree = ""; }; 0F5F08CC146BE602000472A9 /* DFGByteCodeCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGByteCodeCache.h; path = dfg/DFGByteCodeCache.h; sourceTree = ""; }; 0F5F08CE146C762F000472A9 /* UnconditionalFinalizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnconditionalFinalizer.h; sourceTree = ""; }; 0F62016D143FCD2F0068B77C /* DFGAbstractState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGAbstractState.cpp; path = dfg/DFGAbstractState.cpp; sourceTree = ""; }; @@ -2314,6 +2320,7 @@ A7C40C08130B057D00D002A1 /* SentinelLinkedList.h */, 76FB9F1012E851960051A2EB /* SHA1.cpp */, 76FB9F0E12E851860051A2EB /* SHA1.h */, + 0F56A1D6150028B9002992B1 /* SimpleStats.h */, A7C40C09130B057D00D002A1 /* SinglyLinkedList.h */, 0BF28A2811A33DC300638F84 /* SizeLimits.cpp */, 0F2E5BF5146357D2003EB2EB /* Spectrum.h */, @@ -2788,6 +2795,8 @@ 969A078F0ED1D3AE00F1F681 /* bytecode */ = { isa = PBXGroup; children = ( + 0F56A1D415001CF2002992B1 /* ExecutionCounter.cpp */, + 0F56A1D115000F31002992B1 /* ExecutionCounter.h */, 0FB5467C14F5CFD3002C2989 /* MethodOfGettingAValueProfile.cpp */, 0FB5467A14F5C7D4002C2989 /* MethodOfGettingAValueProfile.h */, 0FB5467814F5C468002C2989 /* LazyOperandValueProfile.cpp */, @@ -3409,6 +3418,8 @@ 0FB5467B14F5C7E1002C2989 /* MethodOfGettingAValueProfile.h in Headers */, 0FB5469014FADA7B002C2989 /* RefCountedArray.h in Headers */, 0F0776BF14FF002B00102332 /* JITCompilationEffort.h in Headers */, + 0F56A1D315000F35002992B1 /* ExecutionCounter.h in Headers */, + 0F56A1D7150028BB002992B1 /* SimpleStats.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4018,6 +4029,7 @@ 0FFFC95F14EF90BB00C72532 /* DFGVirtualRegisterAllocationPhase.cpp in Sources */, 0FB5467914F5C46B002C2989 /* LazyOperandValueProfile.cpp in Sources */, 0FB5467D14F5CFD6002C2989 /* MethodOfGettingAValueProfile.cpp in Sources */, + 0F56A1D515001CF4002992B1 /* ExecutionCounter.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/JavaScriptCore/Target.pri b/Source/JavaScriptCore/Target.pri index 8fa498c..4a0c3f8 100644 --- a/Source/JavaScriptCore/Target.pri +++ b/Source/JavaScriptCore/Target.pri @@ -53,6 +53,7 @@ SOURCES += \ bytecode/CallLinkStatus.cpp \ bytecode/CodeBlock.cpp \ bytecode/DFGExitProfile.cpp \ + bytecode/ExecutionCounter.cpp \ bytecode/GetByIdStatus.cpp \ bytecode/JumpTable.cpp \ bytecode/LazyOperandValueProfile.cpp \ diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp index d033941..20972cc 100644 --- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp +++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp @@ -2473,7 +2473,38 @@ void CodeBlock::dumpValueProfiles() } #endif -#ifndef NDEBUG +size_t CodeBlock::predictedMachineCodeSize() +{ + // This will be called from CodeBlock::CodeBlock before either m_globalData or the + // instructions have been initialized. It's OK to return 0 because what will really + // matter is the recomputation of this value when the slow path is triggered. + if (!m_globalData) + return 0; + + if (!m_globalData->machineCodeBytesPerBytecodeWordForBaselineJIT) + return 0; // It's as good of a prediction as we'll get. + + // Be conservative: return a size that will be an overestimation 84% of the time. + double multiplier = m_globalData->machineCodeBytesPerBytecodeWordForBaselineJIT.mean() + + m_globalData->machineCodeBytesPerBytecodeWordForBaselineJIT.standardDeviation(); + + // Be paranoid: silently reject bogus multipiers. Silently doing the "wrong" thing + // here is OK, since this whole method is just a heuristic. + if (multiplier < 0 || multiplier > 1000) + return 0; + + double doubleResult = multiplier * m_instructions.size(); + + // Be even more paranoid: silently reject values that won't fit into a size_t. If + // the function is so huge that we can't even fit it into virtual memory then we + // should probably have some other guards in place to prevent us from even getting + // to this point. + if (doubleResult > std::numeric_limits::max()) + return 0; + + return static_cast(doubleResult); +} + bool CodeBlock::usesOpcode(OpcodeID opcodeID) { Interpreter* interpreter = globalData()->interpreter; @@ -2498,6 +2529,5 @@ bool CodeBlock::usesOpcode(OpcodeID opcodeID) return false; } -#endif } // namespace JSC diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.h b/Source/JavaScriptCore/bytecode/CodeBlock.h index 5ffded7..4690280 100644 --- a/Source/JavaScriptCore/bytecode/CodeBlock.h +++ b/Source/JavaScriptCore/bytecode/CodeBlock.h @@ -41,6 +41,7 @@ #include "DFGOSREntry.h" #include "DFGOSRExit.h" #include "EvalCodeCache.h" +#include "ExecutionCounter.h" #include "ExpressionRangeInfo.h" #include "GlobalResolveInfo.h" #include "HandlerInfo.h" @@ -344,6 +345,8 @@ namespace JSC { RefCountedArray& instructions() { return m_instructions; } const RefCountedArray& instructions() const { return m_instructions; } + size_t predictedMachineCodeSize(); + bool usesOpcode(OpcodeID); unsigned instructionCount() { return m_instructions.size(); } @@ -832,24 +835,29 @@ namespace JSC { // Functions for controlling when JITting kicks in, in a mixed mode // execution world. + bool checkIfJITThresholdReached() + { + return m_llintExecuteCounter.checkIfThresholdCrossedAndSet(this); + } + void dontJITAnytimeSoon() { - m_llintExecuteCounter = Options::executionCounterValueForDontJITAnytimeSoon; + m_llintExecuteCounter.deferIndefinitely(); } void jitAfterWarmUp() { - m_llintExecuteCounter = Options::executionCounterValueForJITAfterWarmUp; + m_llintExecuteCounter.setNewThreshold(Options::thresholdForJITAfterWarmUp, this); } void jitSoon() { - m_llintExecuteCounter = Options::executionCounterValueForJITSoon; + m_llintExecuteCounter.setNewThreshold(Options::thresholdForJITSoon, this); } int32_t llintExecuteCounter() const { - return m_llintExecuteCounter; + return m_llintExecuteCounter.m_counter; } // Functions for controlling when tiered compilation kicks in. This @@ -888,31 +896,41 @@ namespace JSC { int32_t counterValueForOptimizeAfterWarmUp() { - return Options::executionCounterValueForOptimizeAfterWarmUp << reoptimizationRetryCounter(); + return Options::thresholdForOptimizeAfterWarmUp << reoptimizationRetryCounter(); } int32_t counterValueForOptimizeAfterLongWarmUp() { - return Options::executionCounterValueForOptimizeAfterLongWarmUp << reoptimizationRetryCounter(); + return Options::thresholdForOptimizeAfterLongWarmUp << reoptimizationRetryCounter(); } int32_t* addressOfJITExecuteCounter() { - return &m_jitExecuteCounter; + return &m_jitExecuteCounter.m_counter; } - static ptrdiff_t offsetOfJITExecuteCounter() { return OBJECT_OFFSETOF(CodeBlock, m_jitExecuteCounter); } + static ptrdiff_t offsetOfJITExecuteCounter() { return OBJECT_OFFSETOF(CodeBlock, m_jitExecuteCounter) + OBJECT_OFFSETOF(ExecutionCounter, m_counter); } + static ptrdiff_t offsetOfJITExecutionActiveThreshold() { return OBJECT_OFFSETOF(CodeBlock, m_jitExecuteCounter) + OBJECT_OFFSETOF(ExecutionCounter, m_activeThreshold); } + static ptrdiff_t offsetOfJITExecutionTotalCount() { return OBJECT_OFFSETOF(CodeBlock, m_jitExecuteCounter) + OBJECT_OFFSETOF(ExecutionCounter, m_totalCount); } - int32_t jitExecuteCounter() const { return m_jitExecuteCounter; } + int32_t jitExecuteCounter() const { return m_jitExecuteCounter.m_counter; } unsigned optimizationDelayCounter() const { return m_optimizationDelayCounter; } + // Check if the optimization threshold has been reached, and if not, + // adjust the heuristics accordingly. Returns true if the threshold has + // been reached. + bool checkIfOptimizationThresholdReached() + { + return m_jitExecuteCounter.checkIfThresholdCrossedAndSet(this); + } + // Call this to force the next optimization trigger to fire. This is // rarely wise, since optimization triggers are typically more // expensive than executing baseline code. void optimizeNextInvocation() { - m_jitExecuteCounter = Options::executionCounterValueForOptimizeNextInvocation; + m_jitExecuteCounter.setNewThreshold(0, this); } // Call this to prevent optimization from happening again. Note that @@ -922,7 +940,7 @@ namespace JSC { // the future as well. void dontOptimizeAnytimeSoon() { - m_jitExecuteCounter = Options::executionCounterValueForDontOptimizeAnytimeSoon; + m_jitExecuteCounter.deferIndefinitely(); } // Call this to reinitialize the counter to its starting state, @@ -933,14 +951,14 @@ namespace JSC { // counter that this corresponds to is also available directly. void optimizeAfterWarmUp() { - m_jitExecuteCounter = counterValueForOptimizeAfterWarmUp(); + m_jitExecuteCounter.setNewThreshold(counterValueForOptimizeAfterWarmUp(), this); } // Call this to force an optimization trigger to fire only after // a lot of warm-up. void optimizeAfterLongWarmUp() { - m_jitExecuteCounter = counterValueForOptimizeAfterLongWarmUp(); + m_jitExecuteCounter.setNewThreshold(counterValueForOptimizeAfterLongWarmUp(), this); } // Call this to cause an optimization trigger to fire soon, but @@ -963,7 +981,7 @@ namespace JSC { // in the baseline code. void optimizeSoon() { - m_jitExecuteCounter = Options::executionCounterValueForOptimizeSoon << reoptimizationRetryCounter(); + m_jitExecuteCounter.setNewThreshold(Options::thresholdForOptimizeSoon << reoptimizationRetryCounter(), this); } // The speculative JIT tracks its success rate, so that we can @@ -1204,13 +1222,14 @@ namespace JSC { OwnPtr m_alternative; - int32_t m_llintExecuteCounter; + ExecutionCounter m_llintExecuteCounter; - int32_t m_jitExecuteCounter; + ExecutionCounter m_jitExecuteCounter; + int32_t m_totalJITExecutions; uint32_t m_speculativeSuccessCounter; uint32_t m_speculativeFailCounter; - uint8_t m_optimizationDelayCounter; - uint8_t m_reoptimizationRetryCounter; + uint16_t m_optimizationDelayCounter; + uint16_t m_reoptimizationRetryCounter; struct RareData { WTF_MAKE_FAST_ALLOCATED; diff --git a/Source/JavaScriptCore/bytecode/ExecutionCounter.cpp b/Source/JavaScriptCore/bytecode/ExecutionCounter.cpp new file mode 100644 index 0000000..78d02aa --- /dev/null +++ b/Source/JavaScriptCore/bytecode/ExecutionCounter.cpp @@ -0,0 +1,159 @@ +/* + * 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 "ExecutionCounter.h" + +#include "CodeBlock.h" +#include "ExecutableAllocator.h" +#include + +namespace JSC { + +ExecutionCounter::ExecutionCounter() +{ + reset(); +} + +bool ExecutionCounter::checkIfThresholdCrossedAndSet(CodeBlock* codeBlock) +{ + if (hasCrossedThreshold(codeBlock)) + return true; + + if (setThreshold(codeBlock)) + return true; + + return false; +} + +void ExecutionCounter::setNewThreshold(int32_t threshold, CodeBlock* codeBlock) +{ + reset(); + m_activeThreshold = threshold; + setThreshold(codeBlock); +} + +void ExecutionCounter::deferIndefinitely() +{ + m_totalCount = 0; + m_activeThreshold = std::numeric_limits::max(); + m_counter = std::numeric_limits::min(); +} + +double ExecutionCounter::applyMemoryUsageHeuristics(int32_t value, CodeBlock* codeBlock) +{ + double multiplier = + ExecutableAllocator::memoryPressureMultiplier( + codeBlock->predictedMachineCodeSize()); + ASSERT(multiplier >= 1.0); + return multiplier * value; +} + +int32_t ExecutionCounter::applyMemoryUsageHeuristicsAndConvertToInt( + int32_t value, CodeBlock* codeBlock) +{ + double doubleResult = applyMemoryUsageHeuristics(value, codeBlock); + + ASSERT(doubleResult >= 0); + + if (doubleResult > std::numeric_limits::max()) + return std::numeric_limits::max(); + + return static_cast(doubleResult); +} + +bool ExecutionCounter::hasCrossedThreshold(CodeBlock* codeBlock) const +{ + // This checks if the current count rounded up to the threshold we were targeting. + // For example, if we are using half of available executable memory and have + // m_activeThreshold = 1000, applyMemoryUsageHeuristics(m_activeThreshold) will be + // 2000, but we will pretend as if the threshold was crossed if we reach 2000 - + // 1000 / 2, or 1500. The reasoning here is that we want to avoid thrashing. If + // this method returns false, then the JIT's threshold for when it will again call + // into the slow path (which will call this method a second time) will be set + // according to the difference between the current count and the target count + // according to *current* memory usage. But by the time we call into this again, we + // may have JIT'ed more code, and so the target count will increase slightly. This + // may lead to a repeating pattern where the target count is slightly incremented, + // the JIT immediately matches that increase, calls into the slow path again, and + // again the target count is slightly incremented. Instead of having this vicious + // cycle, we declare victory a bit early if the difference between the current + // total and our target according to memory heuristics is small. Our definition of + // small is arbitrarily picked to be half of the original threshold (i.e. + // m_activeThreshold). + + double modifiedThreshold = applyMemoryUsageHeuristics(m_activeThreshold, codeBlock); + + return static_cast(m_totalCount) + m_counter >= + modifiedThreshold - static_cast(m_activeThreshold) / 2; +} + +bool ExecutionCounter::setThreshold(CodeBlock* codeBlock) +{ + if (m_activeThreshold == std::numeric_limits::max()) { + deferIndefinitely(); + return false; + } + + ASSERT(!hasCrossedThreshold(codeBlock)); + + // Compute the true total count. + double trueTotalCount = static_cast(m_totalCount) + m_counter; + + // Correct the threshold for current memory usage. + double threshold = applyMemoryUsageHeuristics(m_activeThreshold, codeBlock); + + // Threshold must be non-negative and not NaN. + ASSERT(threshold >= 0); + + // Adjust the threshold according to the number of executions we have already + // seen. This shouldn't go negative, but it might, because of round-off errors. + threshold -= trueTotalCount; + + if (threshold <= 0) { + m_counter = 0; + m_totalCount = trueTotalCount; + return true; + } + + if (threshold > std::numeric_limits::max()) + threshold = std::numeric_limits::max(); + + m_counter = static_cast(-threshold); + + m_totalCount = trueTotalCount + threshold; + + return false; +} + +void ExecutionCounter::reset() +{ + m_counter = 0; + m_totalCount = 0; + m_activeThreshold = 0; +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/bytecode/ExecutionCounter.h b/Source/JavaScriptCore/bytecode/ExecutionCounter.h new file mode 100644 index 0000000..d2ffbb6 --- /dev/null +++ b/Source/JavaScriptCore/bytecode/ExecutionCounter.h @@ -0,0 +1,83 @@ +/* + * 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 ExecutionCounter_h +#define ExecutionCounter_h + +#include + +namespace JSC { + +class CodeBlock; + +class ExecutionCounter { +public: + ExecutionCounter(); + bool checkIfThresholdCrossedAndSet(CodeBlock*); + void setNewThreshold(int32_t threshold, CodeBlock*); + void deferIndefinitely(); + static double applyMemoryUsageHeuristics(int32_t value, CodeBlock*); + static int32_t applyMemoryUsageHeuristicsAndConvertToInt(int32_t value, CodeBlock*); + + static int32_t formattedTotalCount(float value) + { + union { + int32_t i; + float f; + } u; + u.f = value; + return u.i; + } + +private: + bool hasCrossedThreshold(CodeBlock*) const; + bool setThreshold(CodeBlock*); + void reset(); + +public: + + // NB. These are intentionally public because it will be modified from machine code. + + // This counter is incremented by the JIT or LLInt. It starts out negative and is + // counted up until it becomes non-negative. At the start of a counting period, + // the threshold we wish to reach is m_totalCount + m_counter, in the sense that + // we will add X to m_totalCount and subtract X from m_counter. + int32_t m_counter; + + // Counts the total number of executions we have seen plus the ones we've set a + // threshold for in m_counter. Because m_counter's threshold is negative, the + // total number of actual executions can always be computed as m_totalCount + + // m_counter. + float m_totalCount; + + // This is the threshold we were originally targetting, without any correction for + // the memory usage heuristics. + int32_t m_activeThreshold; +}; + +} // namespace JSC + +#endif // ExecutionCounter_h + diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp index a672234..5d2aa25 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp @@ -581,13 +581,21 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco AssemblyHelpers::Jump lowFailRate = m_jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, GPRInfo::regT1); // Reoptimize as soon as possible. - m_jit.store32(AssemblyHelpers::Imm32(Options::executionCounterValueForOptimizeNextInvocation), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + m_jit.store32(AssemblyHelpers::Imm32(0), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + m_jit.store32(AssemblyHelpers::Imm32(0), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionActiveThreshold())); AssemblyHelpers::Jump doneAdjusting = m_jit.jump(); fewFails.link(&m_jit); lowFailRate.link(&m_jit); - m_jit.store32(AssemblyHelpers::Imm32(m_jit.baselineCodeBlock()->counterValueForOptimizeAfterLongWarmUp()), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + // Adjust the execution counter such that the target is to only optimize after a while. + int32_t targetValue = + ExecutionCounter::applyMemoryUsageHeuristicsAndConvertToInt( + m_jit.baselineCodeBlock()->counterValueForOptimizeAfterLongWarmUp(), + m_jit.baselineCodeBlock()); + m_jit.store32(AssemblyHelpers::Imm32(-targetValue), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + m_jit.store32(AssemblyHelpers::Imm32(targetValue), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionActiveThreshold())); + m_jit.store32(AssemblyHelpers::Imm32(ExecutionCounter::formattedTotalCount(targetValue)), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionTotalCount())); doneAdjusting.link(&m_jit); diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp index f5e0397..7a5b2d4 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp @@ -560,13 +560,21 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco AssemblyHelpers::Jump lowFailRate = m_jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, GPRInfo::regT1); // Reoptimize as soon as possible. - m_jit.store32(AssemblyHelpers::Imm32(Options::executionCounterValueForOptimizeNextInvocation), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + m_jit.store32(AssemblyHelpers::Imm32(0), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + m_jit.store32(AssemblyHelpers::Imm32(0), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionActiveThreshold())); AssemblyHelpers::Jump doneAdjusting = m_jit.jump(); fewFails.link(&m_jit); lowFailRate.link(&m_jit); - m_jit.store32(AssemblyHelpers::Imm32(m_jit.baselineCodeBlock()->counterValueForOptimizeAfterLongWarmUp()), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + // Adjust the execution counter such that the target is to only optimize after a while. + int32_t targetValue = + ExecutionCounter::applyMemoryUsageHeuristicsAndConvertToInt( + m_jit.baselineCodeBlock()->counterValueForOptimizeAfterLongWarmUp(), + m_jit.baselineCodeBlock()); + m_jit.store32(AssemblyHelpers::Imm32(-targetValue), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + m_jit.store32(AssemblyHelpers::Imm32(targetValue), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionActiveThreshold())); + m_jit.store32(AssemblyHelpers::Imm32(ExecutionCounter::formattedTotalCount(targetValue)), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionTotalCount())); doneAdjusting.link(&m_jit); diff --git a/Source/JavaScriptCore/jit/ExecutableAllocator.cpp b/Source/JavaScriptCore/jit/ExecutableAllocator.cpp index 59d3dc3..1af1f42 100644 --- a/Source/JavaScriptCore/jit/ExecutableAllocator.cpp +++ b/Source/JavaScriptCore/jit/ExecutableAllocator.cpp @@ -29,11 +29,17 @@ #if ENABLE(EXECUTABLE_ALLOCATOR_DEMAND) #include "CodeProfiling.h" +#include #include #include #include #endif +// Uncomment to create an artificial executable memory usage limit. This limit +// is imperfect and is primarily useful for testing the VM's ability to handle +// out-of-executable-memory situations. +// #define EXECUTABLE_MEMORY_LIMIT 1000000 + #if ENABLE(ASSEMBLER) using namespace WTF; @@ -65,6 +71,11 @@ protected: numPages = newNumPages; +#ifdef EXECUTABLE_MEMORY_LIMIT + if (bytesAllocated() >= EXECUTABLE_MEMORY_LIMIT) + return 0; +#endif + PageReservation reservation = PageReservation::reserve(numPages * pageSize(), OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true); if (!reservation) CRASH(); @@ -109,7 +120,25 @@ bool ExecutableAllocator::isValid() const bool ExecutableAllocator::underMemoryPressure() { +#ifdef EXECUTABLE_MEMORY_LIMIT + return allocator->bytesAllocated() > EXECUTABLE_MEMORY_LIMIT / 2; +#else return false; +#endif +} + +double ExecutableAllocator::memoryPressureMultiplier(size_t addedMemoryUsage) +{ +#ifdef EXECUTABLE_MEMORY_LIMIT + size_t bytesAllocated = allocator->bytesAllocated() + addedMemoryUsage; + if (bytesAllocated >= EXECUTABLE_MEMORY_LIMIT) + bytesAllocated = EXECUTABLE_MEMORY_LIMIT; + return static_cast(EXECUTABLE_MEMORY_LIMIT) / + (EXECUTABLE_MEMORY_LIMIT - bytesAllocated); +#else + UNUSED_PARAM(addedMemoryUsage); + return 1.0; +#endif } PassRefPtr ExecutableAllocator::allocate(JSGlobalData&, size_t sizeInBytes, void* ownerUID, JITCompilationEffort effort) diff --git a/Source/JavaScriptCore/jit/ExecutableAllocator.h b/Source/JavaScriptCore/jit/ExecutableAllocator.h index 639dd9e..0a05830 100644 --- a/Source/JavaScriptCore/jit/ExecutableAllocator.h +++ b/Source/JavaScriptCore/jit/ExecutableAllocator.h @@ -108,6 +108,8 @@ public: static bool underMemoryPressure(); + static double memoryPressureMultiplier(size_t addedMemoryUsage); + #if ENABLE(META_ALLOCATOR_PROFILE) static void dumpProfile(); #else diff --git a/Source/JavaScriptCore/jit/ExecutableAllocatorFixedVMPool.cpp b/Source/JavaScriptCore/jit/ExecutableAllocatorFixedVMPool.cpp index 96867bf..f093010 100644 --- a/Source/JavaScriptCore/jit/ExecutableAllocatorFixedVMPool.cpp +++ b/Source/JavaScriptCore/jit/ExecutableAllocatorFixedVMPool.cpp @@ -115,6 +115,17 @@ bool ExecutableAllocator::underMemoryPressure() return statistics.bytesAllocated > statistics.bytesReserved / 2; } +double ExecutableAllocator::memoryPressureMultiplier(size_t addedMemoryUsage) +{ + MetaAllocator::Statistics statistics = allocator->currentStatistics(); + ASSERT(statistics.bytesAllocated <= statistics.bytesReserved); + size_t bytesAllocated = statistics.bytesAllocated + addedMemoryUsage; + if (bytesAllocated >= statistics.bytesReserved) + bytesAllocated = statistics.bytesReserved; + return static_cast(statistics.bytesReserved) / + (statistics.bytesReserved - bytesAllocated); +} + PassRefPtr ExecutableAllocator::allocate(JSGlobalData& globalData, size_t sizeInBytes, void* ownerUID, JITCompilationEffort effort) { RefPtr result = allocator->allocate(sizeInBytes, ownerUID); diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp index 2528ab1..84fac12 100644 --- a/Source/JavaScriptCore/jit/JIT.cpp +++ b/Source/JavaScriptCore/jit/JIT.cpp @@ -715,6 +715,10 @@ JITCode JIT::privateCompile(CodePtr* functionEntryArityCheck, JITCompilationEffo CodeRef result = patchBuffer.finalizeCode(); + m_globalData->machineCodeBytesPerBytecodeWordForBaselineJIT.add( + static_cast(result.size()) / + static_cast(m_codeBlock->instructions().size())); + #if ENABLE(JIT_VERBOSE) dataLog("JIT generated code for %p at [%p, %p).\n", m_codeBlock, result.executableMemory()->start(), result.executableMemory()->end()); #endif diff --git a/Source/JavaScriptCore/jit/JITStubs.cpp b/Source/JavaScriptCore/jit/JITStubs.cpp index b6bca9e..eb2382c 100644 --- a/Source/JavaScriptCore/jit/JITStubs.cpp +++ b/Source/JavaScriptCore/jit/JITStubs.cpp @@ -1929,12 +1929,16 @@ DEFINE_STUB_FUNCTION(void, optimize_from_loop) CallFrame* callFrame = stackFrame.callFrame; CodeBlock* codeBlock = callFrame->codeBlock(); - unsigned bytecodeIndex = stackFrame.args[0].int32(); + unsigned bytecodeIndex = stackFrame.args[0].int32(); + #if ENABLE(JIT_VERBOSE_OSR) dataLog("%p: Entered optimize_from_loop with executeCounter = %d, reoptimizationRetryCounter = %u, optimizationDelayCounter = %u\n", codeBlock, codeBlock->jitExecuteCounter(), codeBlock->reoptimizationRetryCounter(), codeBlock->optimizationDelayCounter()); #endif + if (!codeBlock->checkIfOptimizationThresholdReached()) + return; + if (codeBlock->hasOptimizedReplacement()) { #if ENABLE(JIT_VERBOSE_OSR) dataLog("Considering loop OSR into %p(%p) with success/fail %u/%u.\n", codeBlock, codeBlock->replacement(), codeBlock->replacement()->speculativeSuccessCounter(), codeBlock->replacement()->speculativeFailCounter()); @@ -2033,6 +2037,9 @@ DEFINE_STUB_FUNCTION(void, optimize_from_ret) dataLog("Entered optimize_from_ret with executeCounter = %d, reoptimizationRetryCounter = %u, optimizationDelayCounter = %u\n", codeBlock->jitExecuteCounter(), codeBlock->reoptimizationRetryCounter(), codeBlock->optimizationDelayCounter()); #endif + if (!codeBlock->checkIfOptimizationThresholdReached()) + return; + if (codeBlock->hasOptimizedReplacement()) { #if ENABLE(JIT_VERBOSE_OSR) dataLog("Returning from old JIT call frame with optimized replacement %p(%p), with success/fail %u/%u", codeBlock, codeBlock->replacement(), codeBlock->replacement()->speculativeSuccessCounter(), codeBlock->replacement()->speculativeFailCounter()); diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp index 7f4f1a9..43e6d8f 100644 --- a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp +++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp @@ -270,6 +270,13 @@ inline bool shouldJIT(ExecState* exec) // Returns true if we should try to OSR. inline bool jitCompileAndSetHeuristics(CodeBlock* codeBlock, ExecState* exec) { + if (!codeBlock->checkIfJITThresholdReached()) { +#if ENABLE(JIT_VERBOSE_OSR) + dataLog(" JIT threshold should be lifted.\n"); +#endif + return false; + } + CodeBlock::JITCompilationResult result = codeBlock->jitCompile(exec->globalData()); switch (result) { case CodeBlock::AlreadyCompiled: diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm index a4fbfbf..bf9ec51 100644 --- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm +++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm @@ -273,7 +273,7 @@ end macro checkSwitchToJIT(increment, action) if JIT_ENABLED loadp CodeBlock[cfr], t0 - baddis increment, CodeBlock::m_llintExecuteCounter[t0], .continue + baddis increment, CodeBlock::m_llintExecuteCounter + ExecutionCounter::m_counter[t0], .continue action() .continue: end @@ -429,7 +429,7 @@ macro prologue(codeBlockGetter, codeBlockSetter, osrSlowPath, traceSlowPath) end codeBlockGetter(t1) if JIT_ENABLED - baddis 5, CodeBlock::m_llintExecuteCounter[t1], .continue + baddis 5, CodeBlock::m_llintExecuteCounter + ExecutionCounter::m_counter[t1], .continue cCall2(osrSlowPath, cfr, PC) move t1, cfr btpz t0, .recover diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.h b/Source/JavaScriptCore/runtime/JSGlobalData.h index 7e54c00..cf14b2d 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.h +++ b/Source/JavaScriptCore/runtime/JSGlobalData.h @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #if ENABLE(REGEXP_TRACING) @@ -201,6 +202,7 @@ namespace JSC { SmallStrings smallStrings; NumericStrings numericStrings; DateInstanceCache dateInstanceCache; + WTF::SimpleStats machineCodeBytesPerBytecodeWordForBaselineJIT; Vector codeBlocksBeingCompiled; void startedCompiling(CodeBlock* codeBlock) { diff --git a/Source/JavaScriptCore/runtime/Options.cpp b/Source/JavaScriptCore/runtime/Options.cpp index e32ec3d..a29d633 100644 --- a/Source/JavaScriptCore/runtime/Options.cpp +++ b/Source/JavaScriptCore/runtime/Options.cpp @@ -52,15 +52,12 @@ unsigned maximumFunctionForConstructInlineCandidateInstructionCount; unsigned maximumInliningDepth; -int32_t executionCounterValueForJITAfterWarmUp; -int32_t executionCounterValueForDontJITAnytimeSoon; -int32_t executionCounterValueForJITSoon; +int32_t thresholdForJITAfterWarmUp; +int32_t thresholdForJITSoon; -int32_t executionCounterValueForOptimizeAfterWarmUp; -int32_t executionCounterValueForOptimizeAfterLongWarmUp; -int32_t executionCounterValueForDontOptimizeAnytimeSoon; -int32_t executionCounterValueForOptimizeSoon; -int32_t executionCounterValueForOptimizeNextInvocation; +int32_t thresholdForOptimizeAfterWarmUp; +int32_t thresholdForOptimizeAfterLongWarmUp; +int32_t thresholdForOptimizeSoon; int32_t executionCounterIncrementForLoop; int32_t executionCounterIncrementForReturn; @@ -141,15 +138,12 @@ void initializeOptions() SET(maximumInliningDepth, 5); - SET(executionCounterValueForJITAfterWarmUp, -100); - SET(executionCounterValueForDontJITAnytimeSoon, std::numeric_limits::min()); - SET(executionCounterValueForJITSoon, -100); + SET(thresholdForJITAfterWarmUp, 100); + SET(thresholdForJITSoon, 100); - SET(executionCounterValueForOptimizeAfterWarmUp, -1000); - SET(executionCounterValueForOptimizeAfterLongWarmUp, -5000); - SET(executionCounterValueForDontOptimizeAnytimeSoon, std::numeric_limits::min()); - SET(executionCounterValueForOptimizeSoon, -1000); - SET(executionCounterValueForOptimizeNextInvocation, 0); + SET(thresholdForOptimizeAfterWarmUp, 1000); + SET(thresholdForOptimizeAfterLongWarmUp, 5000); + SET(thresholdForOptimizeSoon, 1000); SET(executionCounterIncrementForLoop, 1); SET(executionCounterIncrementForReturn, 15); @@ -195,11 +189,9 @@ void initializeOptions() SET(numberOfGCMarkers, cpusToUse); - ASSERT(executionCounterValueForDontOptimizeAnytimeSoon <= executionCounterValueForOptimizeAfterLongWarmUp); - ASSERT(executionCounterValueForOptimizeAfterLongWarmUp <= executionCounterValueForOptimizeAfterWarmUp); - ASSERT(executionCounterValueForOptimizeAfterWarmUp <= executionCounterValueForOptimizeSoon); - ASSERT(executionCounterValueForOptimizeAfterWarmUp < 0); - ASSERT(executionCounterValueForOptimizeSoon <= executionCounterValueForOptimizeNextInvocation); + ASSERT(thresholdForOptimizeAfterLongWarmUp >= thresholdForOptimizeAfterWarmUp); + ASSERT(thresholdForOptimizeAfterWarmUp >= thresholdForOptimizeSoon); + ASSERT(thresholdForOptimizeAfterWarmUp >= 0); // Compute the maximum value of the reoptimization retry counter. This is simply // the largest value at which we don't overflow the execute counter, when using it @@ -207,11 +199,11 @@ void initializeOptions() // up being 18, so this loop is not so terrible; it probably takes up ~100 cycles // total on a 32-bit processor. reoptimizationRetryCounterMax = 0; - while ((static_cast(executionCounterValueForOptimizeAfterLongWarmUp) << (reoptimizationRetryCounterMax + 1)) >= static_cast(std::numeric_limits::min())) + while ((static_cast(thresholdForOptimizeAfterLongWarmUp) << (reoptimizationRetryCounterMax + 1)) <= static_cast(std::numeric_limits::max())) reoptimizationRetryCounterMax++; - ASSERT((static_cast(executionCounterValueForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) < 0); - ASSERT((static_cast(executionCounterValueForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) >= static_cast(std::numeric_limits::min())); + ASSERT((static_cast(thresholdForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) > 0); + ASSERT((static_cast(thresholdForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) <= static_cast(std::numeric_limits::max())); } } } // namespace JSC::Options diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h index b9e68f9..a663ea6 100644 --- a/Source/JavaScriptCore/runtime/Options.h +++ b/Source/JavaScriptCore/runtime/Options.h @@ -37,15 +37,13 @@ extern unsigned maximumFunctionForConstructInlineCandidateInstructionCount; extern unsigned maximumInliningDepth; // Depth of inline stack, so 1 = no inlining, 2 = one level, etc. -extern int32_t executionCounterValueForJITAfterWarmUp; -extern int32_t executionCounterValueForDontJITAnytimeSoon; -extern int32_t executionCounterValueForJITSoon; - -extern int32_t executionCounterValueForOptimizeAfterWarmUp; -extern int32_t executionCounterValueForOptimizeAfterLongWarmUp; -extern int32_t executionCounterValueForDontOptimizeAnytimeSoon; -extern int32_t executionCounterValueForOptimizeSoon; -extern int32_t executionCounterValueForOptimizeNextInvocation; +extern int32_t thresholdForJITAfterWarmUp; +extern int32_t thresholdForJITSoon; + +extern int32_t thresholdForOptimizeAfterWarmUp; +extern int32_t thresholdForOptimizeAfterLongWarmUp; +extern int32_t thresholdForOptimizeSoon; +extern int32_t thresholdForOptimizeNextInvocation; extern int32_t executionCounterIncrementForLoop; extern int32_t executionCounterIncrementForReturn; diff --git a/Source/JavaScriptCore/wtf/SimpleStats.h b/Source/JavaScriptCore/wtf/SimpleStats.h new file mode 100644 index 0000000..7b03c8d --- /dev/null +++ b/Source/JavaScriptCore/wtf/SimpleStats.h @@ -0,0 +1,107 @@ +/* + * 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 SimpleStats_h +#define SimpleStats_h + +#include "MathExtras.h" +#include "StdLibExtras.h" + +namespace WTF { + +// Simple and cheap way of tracking statistics if you're not worried about chopping on +// the sum of squares (i.e. the sum of squares is unlikely to exceed 2^52). +class SimpleStats { +public: + SimpleStats() + : m_count(0) + , m_sum(0) + , m_sumOfSquares(0) + { + } + + void add(double value) + { + m_count++; + m_sum += value; + m_sumOfSquares += value * value; + } + + bool operator!() const + { + return !m_count; + } + + double count() const + { + return m_count; + } + + double sum() const + { + return m_sum; + } + + double sumOfSquares() const + { + return m_sumOfSquares; + } + + double mean() const + { + return m_sum / m_count; + } + + // NB. This gives a biased variance as it divides by the number of samples rather + // than the degrees of freedom. This is fine once the count grows large, which in + // our case will happen rather quickly. + double variance() const + { + if (m_count < 2) + return 0; + + // Compute - ^2 + double secondMoment = m_sumOfSquares / m_count; + double firstMoment = m_sum / m_count; + + return secondMoment - firstMoment * firstMoment; + } + + // NB. This gives a biased standard deviation. See above. + double standardDeviation() const + { + return sqrt(variance()); + } + +private: + double m_count; + double m_sum; + double m_sumOfSquares; +}; + +} // namespace WTF + +#endif // SimpleStats_h + diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog index 4b4a49d..12525be 100644 --- a/Source/WebCore/ChangeLog +++ b/Source/WebCore/ChangeLog @@ -1,3 +1,15 @@ +2012-03-04 Filip Pizlo + + JIT heuristics should be hyperbolic + https://bugs.webkit.org/show_bug.cgi?id=80055 + + + Reviewed by Oliver Hunt. + + No new tests, since there's no new functionality. + + * ForwardingHeaders/wtf/SimpleStats.h: Added. + 2012-03-04 Shinya Kawanaka Methods like firstRendererOf of NodeRenderingContext should be extracted. diff --git a/Source/WebCore/ForwardingHeaders/wtf/SimpleStats.h b/Source/WebCore/ForwardingHeaders/wtf/SimpleStats.h new file mode 100644 index 0000000..9c2d7e6 --- /dev/null +++ b/Source/WebCore/ForwardingHeaders/wtf/SimpleStats.h @@ -0,0 +1,4 @@ +#ifndef WebCore_FWD_SimpleStats_h +#define WebCore_FWD_SimpleStats_h +#include +#endif -- 2.7.4