Bug 631951 - Shrink methodjit memory usage by interpreting a few times before compiling (r=dvander)

This commit is contained in:
Bill McCloskey 2011-02-11 16:31:32 -08:00
parent f174853c0f
commit 42e73fa7bc
28 changed files with 259 additions and 56 deletions

View File

@ -354,6 +354,7 @@ user_pref("browser.EULA.override", true);
user_pref("javascript.options.tracejit.content", true); user_pref("javascript.options.tracejit.content", true);
user_pref("javascript.options.methodjit.content", true); user_pref("javascript.options.methodjit.content", true);
user_pref("javascript.options.jitprofiling.content", true); user_pref("javascript.options.jitprofiling.content", true);
user_pref("javascript.options.methodjit_always", false);
user_pref("gfx.color_management.force_srgb", true); user_pref("gfx.color_management.force_srgb", true);
user_pref("network.manage-offline-status", false); user_pref("network.manage-offline-status", false);
user_pref("test.mousescroll", true); user_pref("test.mousescroll", true);

View File

@ -1015,10 +1015,11 @@ static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal";
#endif #endif
static const char js_tracejit_content_str[] = JS_OPTIONS_DOT_STR "tracejit.content"; static const char js_tracejit_content_str[] = JS_OPTIONS_DOT_STR "tracejit.content";
static const char js_tracejit_chrome_str[] = JS_OPTIONS_DOT_STR "tracejit.chrome"; static const char js_tracejit_chrome_str[] = JS_OPTIONS_DOT_STR "tracejit.chrome";
static const char js_methodjit_content_str[] = JS_OPTIONS_DOT_STR "methodjit.content"; static const char js_methodjit_content_str[] = JS_OPTIONS_DOT_STR "methodjit.content";
static const char js_methodjit_chrome_str[] = JS_OPTIONS_DOT_STR "methodjit.chrome"; static const char js_methodjit_chrome_str[] = JS_OPTIONS_DOT_STR "methodjit.chrome";
static const char js_profiling_content_str[] = JS_OPTIONS_DOT_STR "jitprofiling.content"; static const char js_profiling_content_str[] = JS_OPTIONS_DOT_STR "jitprofiling.content";
static const char js_profiling_chrome_str[] = JS_OPTIONS_DOT_STR "jitprofiling.chrome"; static const char js_profiling_chrome_str[] = JS_OPTIONS_DOT_STR "jitprofiling.chrome";
static const char js_methodjit_always_str[] = JS_OPTIONS_DOT_STR "methodjit_always";
int int
nsJSContext::JSOptionChangedCallback(const char *pref, void *data) nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
@ -1047,6 +1048,7 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
PRBool useProfiling = nsContentUtils::GetBoolPref(chromeWindow ? PRBool useProfiling = nsContentUtils::GetBoolPref(chromeWindow ?
js_profiling_chrome_str : js_profiling_chrome_str :
js_profiling_content_str); js_profiling_content_str);
PRBool useMethodJITAlways = nsContentUtils::GetBoolPref(js_methodjit_always_str);
nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID); nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
if (xr) { if (xr) {
PRBool safeMode = PR_FALSE; PRBool safeMode = PR_FALSE;
@ -1055,6 +1057,7 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
useTraceJIT = PR_FALSE; useTraceJIT = PR_FALSE;
useMethodJIT = PR_FALSE; useMethodJIT = PR_FALSE;
useProfiling = PR_FALSE; useProfiling = PR_FALSE;
useMethodJITAlways = PR_TRUE;
} }
} }
@ -1073,6 +1076,11 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
else else
newDefaultJSOptions &= ~JSOPTION_PROFILING; newDefaultJSOptions &= ~JSOPTION_PROFILING;
if (useMethodJITAlways)
newDefaultJSOptions |= JSOPTION_METHODJIT_ALWAYS;
else
newDefaultJSOptions &= ~JSOPTION_METHODJIT_ALWAYS;
#ifdef DEBUG #ifdef DEBUG
// In debug builds, warnings are enabled in chrome context if // In debug builds, warnings are enabled in chrome context if
// javascript.options.strict.debug is true // javascript.options.strict.debug is true

View File

@ -93,6 +93,8 @@ class Test:
test.allow_oom = True test.allow_oom = True
elif name == 'valgrind': elif name == 'valgrind':
test.valgrind = options.valgrind test.valgrind = options.valgrind
elif name == 'mjitalways':
test.jitflags.append('-a')
else: else:
print('warning: unrecognized |jit-test| attribute %s'%part) print('warning: unrecognized |jit-test| attribute %s'%part)
@ -426,7 +428,7 @@ def main(argv):
for test in test_list: for test in test_list:
for jitflags in jitflags_list: for jitflags in jitflags_list:
new_test = test.copy() new_test = test.copy()
new_test.jitflags = jitflags new_test.jitflags.extend(jitflags)
job_list.append(new_test) job_list.append(new_test)

View File

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true); setDebug(true);
function callee() { function callee() {

View File

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true); setDebug(true);
function callee() { function callee() {

View File

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true); setDebug(true);
function callee() { function callee() {

View File

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true); setDebug(true);
this.__defineGetter__("someProperty", function () { evalInFrame(1, "var x = 'success'"); }); this.__defineGetter__("someProperty", function () { evalInFrame(1, "var x = 'success'"); });

View File

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true); setDebug(true);
this.__defineGetter__("someProperty", function () { evalInFrame(1, "var x = 'success'"); }); this.__defineGetter__("someProperty", function () { evalInFrame(1, "var x = 'success'"); });

View File

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true); setDebug(true);
this.__defineGetter__("someProperty", function () { evalInFrame(1, "x = 'success'"); }); this.__defineGetter__("someProperty", function () { evalInFrame(1, "x = 'success'"); });

View File

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true); setDebug(true);
function nop(){} function nop(){}

View File

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true); setDebug(true);
function nop(){} function nop(){}

View File

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true); setDebug(true);
function nop(){} function nop(){}

View File

@ -1,3 +1,4 @@
// |jit-test| mjitalways
// vim: set ts=4 sw=4 tw=99 et: // vim: set ts=4 sw=4 tw=99 et:
function assertObjectsEqual(obj1, obj2) { function assertObjectsEqual(obj1, obj2) {

View File

@ -955,11 +955,14 @@ JS_StringToVersion(const char *string);
#define JSOPTION_METHODJIT JS_BIT(14) /* Whole-method JIT. */ #define JSOPTION_METHODJIT JS_BIT(14) /* Whole-method JIT. */
#define JSOPTION_PROFILING JS_BIT(15) /* Profiler to make tracer/methodjit choices. */ #define JSOPTION_PROFILING JS_BIT(15) /* Profiler to make tracer/methodjit choices. */
#define JSOPTION_METHODJIT_ALWAYS \
JS_BIT(16) /* Always whole-method JIT,
don't tune at run-time. */
/* Options which reflect compile-time properties of scripts. */ /* Options which reflect compile-time properties of scripts. */
#define JSCOMPILEOPTION_MASK (JSOPTION_XML | JSOPTION_ANONFUNFIX) #define JSCOMPILEOPTION_MASK (JSOPTION_XML | JSOPTION_ANONFUNFIX)
#define JSRUNOPTION_MASK (JS_BITMASK(16) & ~JSCOMPILEOPTION_MASK) #define JSRUNOPTION_MASK (JS_BITMASK(17) & ~JSCOMPILEOPTION_MASK)
#define JSALLOPTION_MASK (JSCOMPILEOPTION_MASK | JSRUNOPTION_MASK) #define JSALLOPTION_MASK (JSCOMPILEOPTION_MASK | JSRUNOPTION_MASK)
extern JS_PUBLIC_API(uint32) extern JS_PUBLIC_API(uint32)

View File

@ -154,10 +154,12 @@ JSCompartment::init()
return false; return false;
#endif #endif
#ifdef JS_METHODJIT if (!backEdgeTable.init())
if (!(jaegerCompartment = js_new<mjit::JaegerCompartment>())) { return false;
#ifdef JS_METHODJIT
if (!(jaegerCompartment = js_new<mjit::JaegerCompartment>()))
return false; return false;
}
return jaegerCompartment->Initialize(); return jaegerCompartment->Initialize();
#else #else
return true; return true;
@ -573,3 +575,25 @@ JSCompartment::allocMathCache(JSContext *cx)
js_ReportOutOfMemory(cx); js_ReportOutOfMemory(cx);
return mathCache; return mathCache;
} }
size_t
JSCompartment::backEdgeCount(jsbytecode *pc) const
{
if (BackEdgeMap::Ptr p = backEdgeTable.lookup(pc))
return p->value;
return 0;
}
size_t
JSCompartment::incBackEdgeCount(jsbytecode *pc)
{
if (BackEdgeMap::AddPtr p = backEdgeTable.lookupForAdd(pc)) {
p->value++;
return p->value;
} else {
backEdgeTable.add(p, pc, 1);
return 1;
}
}

View File

@ -482,6 +482,13 @@ struct JS_FRIEND_API(JSCompartment) {
bool marked; bool marked;
typedef js::HashMap<jsbytecode*,
size_t,
js::DefaultHasher<jsbytecode*>,
js::SystemAllocPolicy> BackEdgeMap;
BackEdgeMap backEdgeTable;
public: public:
js::MathCache *getMathCache(JSContext *cx) { js::MathCache *getMathCache(JSContext *cx) {
return mathCache ? mathCache : allocMathCache(cx); return mathCache ? mathCache : allocMathCache(cx);
@ -489,6 +496,9 @@ struct JS_FRIEND_API(JSCompartment) {
bool isMarked() { return marked; } bool isMarked() { return marked; }
void clearMark() { marked = false; } void clearMark() { marked = false; }
size_t backEdgeCount(jsbytecode *pc) const;
size_t incBackEdgeCount(jsbytecode *pc);
}; };
#define JS_SCRIPTS_TO_GC(cx) ((cx)->compartment->scriptsToGC) #define JS_SCRIPTS_TO_GC(cx) ((cx)->compartment->scriptsToGC)

View File

@ -76,6 +76,7 @@
#include "jslibmath.h" #include "jslibmath.h"
#include "jsvector.h" #include "jsvector.h"
#include "methodjit/MethodJIT.h" #include "methodjit/MethodJIT.h"
#include "methodjit/MethodJIT-inl.h"
#include "methodjit/Logging.h" #include "methodjit/Logging.h"
#include "jsatominlines.h" #include "jsatominlines.h"
@ -629,7 +630,8 @@ RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp)
JS_ASSERT(fp->script() == script); JS_ASSERT(fp->script() == script);
#ifdef JS_METHODJIT #ifdef JS_METHODJIT
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, fp); mjit::CompileStatus status =
mjit::CanMethodJIT(cx, script, fp, mjit::CompileRequest_Interpreter);
if (status == mjit::Compile_Error) if (status == mjit::Compile_Error)
return JS_FALSE; return JS_FALSE;
@ -772,7 +774,7 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this
#ifdef JS_METHODJIT #ifdef JS_METHODJIT
/* Hoist dynamic checks from RunScript. */ /* Hoist dynamic checks from RunScript. */
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp); mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp, mjit::CompileRequest_JIT);
if (status == mjit::Compile_Error) if (status == mjit::Compile_Error)
return false; return false;
if (status != mjit::Compile_Okay) if (status != mjit::Compile_Okay)
@ -2322,6 +2324,30 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
#define LOAD_DOUBLE(PCOFF, dbl) \ #define LOAD_DOUBLE(PCOFF, dbl) \
(dbl = script->getConst(GET_FULL_INDEX(PCOFF)).toDouble()) (dbl = script->getConst(GET_FULL_INDEX(PCOFF)).toDouble())
#ifdef JS_METHODJIT
#define MONITOR_BRANCH_METHODJIT() \
JS_BEGIN_MACRO \
mjit::CompileStatus status = \
mjit::CanMethodJITAtBranch(cx, script, regs.fp, regs.pc); \
if (status == mjit::Compile_Error) \
goto error; \
if (status == mjit::Compile_Okay) { \
void *ncode = \
script->nativeCodeForPC(regs.fp->isConstructing(), regs.pc); \
interpReturnOK = mjit::JaegerShotAtSafePoint(cx, ncode); \
if (inlineCallCount) \
goto jit_return; \
goto leave_on_safe_point; \
} \
JS_END_MACRO
#else
#define MONITOR_BRANCH_METHODJIT() ((void) 0)
#endif
#ifdef JS_TRACER #ifdef JS_TRACER
#ifdef MOZ_TRACEVIS #ifdef MOZ_TRACEVIS
@ -2354,18 +2380,24 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
#define MONITOR_BRANCH() \ #define MONITOR_BRANCH() \
JS_BEGIN_MACRO \ JS_BEGIN_MACRO \
if (TRACING_ENABLED(cx)) { \ if (TRACING_ENABLED(cx)) { \
MonitorResult r = MonitorLoopEdge(cx, inlineCallCount); \ if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && \
if (r == MONITOR_RECORDING) { \ interpMode == JSINTERP_NORMAL) \
JS_ASSERT(TRACE_RECORDER(cx)); \ { \
JS_ASSERT(!TRACE_PROFILER(cx)); \ MONITOR_BRANCH_METHODJIT(); \
MONITOR_BRANCH_TRACEVIS; \ } else { \
ENABLE_INTERRUPTS(); \ MonitorResult r = MonitorLoopEdge(cx, inlineCallCount); \
CLEAR_LEAVE_ON_TRACE_POINT(); \ if (r == MONITOR_RECORDING) { \
JS_ASSERT(TRACE_RECORDER(cx)); \
JS_ASSERT(!TRACE_PROFILER(cx)); \
MONITOR_BRANCH_TRACEVIS; \
ENABLE_INTERRUPTS(); \
CLEAR_LEAVE_ON_TRACE_POINT(); \
} \
RESTORE_INTERP_VARS(); \
JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR); \
if (r == MONITOR_ERROR) \
goto error; \
} \ } \
RESTORE_INTERP_VARS(); \
JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR); \
if (r == MONITOR_ERROR) \
goto error; \
} \ } \
JS_END_MACRO JS_END_MACRO
@ -4693,7 +4725,10 @@ BEGIN_CASE(JSOP_FUNCALL)
#ifdef JS_METHODJIT #ifdef JS_METHODJIT
/* Try to ensure methods are method JIT'd. */ /* Try to ensure methods are method JIT'd. */
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp); mjit::CompileRequest request = (interpMode == JSINTERP_NORMAL)
? mjit::CompileRequest_Interpreter
: mjit::CompileRequest_JIT;
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp, request);
if (status == mjit::Compile_Error) if (status == mjit::Compile_Error)
goto error; goto error;
if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && status == mjit::Compile_Okay) { if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && status == mjit::Compile_Okay) {
@ -6920,7 +6955,7 @@ END_CASE(JSOP_ARRAYPUSH)
* This path is used when it's guaranteed the method can be finished * This path is used when it's guaranteed the method can be finished
* inside the JIT. * inside the JIT.
*/ */
#if defined(JS_TRACER) && defined(JS_METHODJIT) #if defined(JS_METHODJIT)
leave_on_safe_point: leave_on_safe_point:
#endif #endif
return interpReturnOK; return interpReturnOK;

View File

@ -373,6 +373,8 @@ struct JSScript {
private: private:
uint16 version; /* JS version under which script was compiled */ uint16 version; /* JS version under which script was compiled */
size_t callCount_; /* Number of times the script has been called. */
public: public:
uint16 nfixed; /* number of slots besides stack operands in uint16 nfixed; /* number of slots besides stack operands in
slot array */ slot array */
@ -473,6 +475,9 @@ struct JSScript {
return constructing ? jitCtor : jitNormal; return constructing ? jitCtor : jitNormal;
} }
size_t callCount() const { return callCount_; }
size_t incCallCount() { return ++callCount_; }
JITScriptStatus getJITStatus(bool constructing) { JITScriptStatus getJITStatus(bool constructing) {
void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal; void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal;
if (addr == NULL) if (addr == NULL)

View File

@ -645,7 +645,8 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
#ifdef JS_TRACER #ifdef JS_TRACER
jitTraceICs[i].loopCounterStart = GetHotloop(cx); jitTraceICs[i].loopCounterStart = GetHotloop(cx);
#endif #endif
jitTraceICs[i].loopCounter = jitTraceICs[i].loopCounterStart; jitTraceICs[i].loopCounter = jitTraceICs[i].loopCounterStart
- cx->compartment->backEdgeCount(traceICs[i].jumpTarget);
stubCode.patch(traceICs[i].addrLabel, &jitTraceICs[i]); stubCode.patch(traceICs[i].addrLabel, &jitTraceICs[i]);
} }

View File

@ -68,6 +68,7 @@
#include "jscntxtinlines.h" #include "jscntxtinlines.h"
#include "jsatominlines.h" #include "jsatominlines.h"
#include "StubCalls-inl.h" #include "StubCalls-inl.h"
#include "MethodJIT-inl.h"
#include "jsautooplen.h" #include "jsautooplen.h"
@ -170,12 +171,7 @@ top:
} }
/* /*
* Clean up a frame and return. popFrame indicates whether to additionally pop * Clean up a frame and return.
* the frame and store the return value on the caller's stack. The frame will
* normally be popped by the caller on return from a call into JIT code,
* so must be popped here when that caller code will not execute. This can be
* either because of a call into an un-JITable script, or because the call is
* throwing an exception.
*/ */
static void static void
InlineReturn(VMFrame &f) InlineReturn(VMFrame &f)
@ -311,8 +307,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
/* /*
* FixupArity/RemovePartialFrame expect to be called after the early * FixupArity/RemovePartialFrame expect to be called after the early
* prologue. Pass the existing value for ncode, it has already been set * prologue.
* by the jit code calling into this stub.
*/ */
fp->initCallFrameEarlyPrologue(fun, nactual); fp->initCallFrameEarlyPrologue(fun, nactual);
@ -333,7 +328,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
if (fun->isHeavyweight() && !js_GetCallObject(cx, fp)) if (fun->isHeavyweight() && !js_GetCallObject(cx, fp))
THROWV(NULL); THROWV(NULL);
CompileStatus status = CanMethodJIT(cx, script, fp); CompileStatus status = CanMethodJIT(cx, script, fp, CompileRequest_JIT);
if (status == Compile_Okay) if (status == Compile_Okay)
return script->getJIT(fp->isConstructing())->invokeEntry; return script->getJIT(fp->isConstructing())->invokeEntry;
@ -348,7 +343,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
} }
static inline bool static inline bool
UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc) UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, bool *unjittable, uint32 argc)
{ {
JSContext *cx = f.cx; JSContext *cx = f.cx;
Value *vp = f.regs.sp - (argc + 2); Value *vp = f.regs.sp - (argc + 2);
@ -380,11 +375,14 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
/* Try to compile if not already compiled. */ /* Try to compile if not already compiled. */
if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) { if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) {
if (mjit::TryCompile(cx, newfp) == Compile_Error) { CompileStatus status = CanMethodJIT(cx, newscript, newfp, CompileRequest_Interpreter);
if (status == Compile_Error) {
/* A runtime exception was thrown, get out. */ /* A runtime exception was thrown, get out. */
InlineReturn(f); InlineReturn(f);
return false; return false;
} }
if (status == Compile_Abort)
*unjittable = true;
} }
/* If newscript was successfully compiled, run it. */ /* If newscript was successfully compiled, run it. */
@ -420,7 +418,7 @@ stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
/* Try to do a fast inline call before the general Invoke path. */ /* Try to do a fast inline call before the general Invoke path. */
if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted()) { if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted()) {
ucr->callee = &vp->toObject(); ucr->callee = &vp->toObject();
if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, argc)) if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, &ucr->unjittable, argc))
THROW(); THROW();
} else { } else {
if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc))) if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
@ -470,7 +468,7 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
ucr->fun = GET_FUNCTION_PRIVATE(cx, ucr->callee); ucr->fun = GET_FUNCTION_PRIVATE(cx, ucr->callee);
if (ucr->fun->isInterpreted()) { if (ucr->fun->isInterpreted()) {
if (!UncachedInlineCall(f, 0, &ucr->codeAddr, argc)) if (!UncachedInlineCall(f, 0, &ucr->codeAddr, &ucr->unjittable, argc))
THROW(); THROW();
return; return;
} }

View File

@ -0,0 +1,105 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
* David Anderson <danderson@mozilla.com>
* David Mandelin <dmandelin@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#if !defined jsjaeger_methodjit_inl_h__ && defined JS_METHODJIT
#define jsjaeger_methodjit_inl_h__
namespace js {
namespace mjit {
enum CompileRequest
{
CompileRequest_Interpreter,
CompileRequest_JIT
};
/* Number of times a script must be called before we run it in the methodjit. */
static const size_t CALLS_BEFORE_COMPILE = 16;
/* Number of loop back-edges we execute in the interpreter before methodjitting. */
static const size_t BACKEDGES_BEFORE_COMPILE = 16;
static inline CompileStatus
CanMethodJIT(JSContext *cx, JSScript *script, JSStackFrame *fp, CompileRequest request)
{
if (!cx->methodJitEnabled)
return Compile_Abort;
JITScriptStatus status = script->getJITStatus(fp->isConstructing());
if (status == JITScript_Invalid)
return Compile_Abort;
if (request == CompileRequest_Interpreter &&
status == JITScript_None &&
!cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
script->incCallCount() <= CALLS_BEFORE_COMPILE)
{
return Compile_Skipped;
}
if (status == JITScript_None)
return TryCompile(cx, fp);
return Compile_Okay;
}
/*
* Called from a backedge in the interpreter to decide if we should transition to the
* methodjit. If so, we compile the given function.
*/
static inline CompileStatus
CanMethodJITAtBranch(JSContext *cx, JSScript *script, JSStackFrame *fp, jsbytecode *pc)
{
if (!cx->methodJitEnabled)
return Compile_Abort;
JITScriptStatus status = script->getJITStatus(fp->isConstructing());
if (status == JITScript_Invalid)
return Compile_Abort;
if (status == JITScript_None &&
!cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
cx->compartment->incBackEdgeCount(pc) <= BACKEDGES_BEFORE_COMPILE)
{
return Compile_Skipped;
}
if (status == JITScript_None)
return TryCompile(cx, fp);
return Compile_Okay;
}
}
}
#endif

View File

@ -394,7 +394,8 @@ enum CompileStatus
{ {
Compile_Okay, Compile_Okay,
Compile_Abort, Compile_Abort,
Compile_Error Compile_Error,
Compile_Skipped
}; };
void JS_FASTCALL void JS_FASTCALL
@ -406,19 +407,6 @@ TryCompile(JSContext *cx, JSStackFrame *fp);
void void
ReleaseScriptCode(JSContext *cx, JSScript *script); ReleaseScriptCode(JSContext *cx, JSScript *script);
static inline CompileStatus
CanMethodJIT(JSContext *cx, JSScript *script, JSStackFrame *fp)
{
if (!cx->methodJitEnabled)
return Compile_Abort;
JITScriptStatus status = script->getJITStatus(fp->isConstructing());
if (status == JITScript_Invalid)
return Compile_Abort;
if (status == JITScript_None)
return TryCompile(cx, fp);
return Compile_Okay;
}
struct CallSite struct CallSite
{ {
uint32 codeOffset; uint32 codeOffset;

View File

@ -950,7 +950,8 @@ class CallCompiler : public BaseCompiler
// If the function cannot be jitted (generally unjittable or empty script), // If the function cannot be jitted (generally unjittable or empty script),
// patch this site to go to a slow path always. // patch this site to go to a slow path always.
if (!ucr.codeAddr) { if (!ucr.codeAddr) {
disable(jit); if (ucr.unjittable)
disable(jit);
return NULL; return NULL;
} }

View File

@ -80,7 +80,7 @@ void JS_FASTCALL LeaveScript(VMFrame &f);
* These functions can have one of two results: * These functions can have one of two results:
* *
* (1) The function was executed in the interpreter. Then all fields * (1) The function was executed in the interpreter. Then all fields
* are NULL. * are NULL except unjittable.
* *
* (2) The function was not executed, and the function has been compiled * (2) The function was not executed, and the function has been compiled
* to JM native code. Then all fields are non-NULL. * to JM native code. Then all fields are non-NULL.
@ -89,11 +89,13 @@ struct UncachedCallResult {
JSObject *callee; // callee object JSObject *callee; // callee object
JSFunction *fun; // callee function JSFunction *fun; // callee function
void *codeAddr; // code address of compiled callee function void *codeAddr; // code address of compiled callee function
bool unjittable; // did we try to JIT and fail?
void init() { void init() {
callee = NULL; callee = NULL;
fun = NULL; fun = NULL;
codeAddr = NULL; codeAddr = NULL;
unjittable = false;
} }
}; };

View File

@ -582,6 +582,8 @@ usage(void)
" -i Enable interactive read-eval-print loop\n" " -i Enable interactive read-eval-print loop\n"
" -j Enable the TraceMonkey tracing JIT\n" " -j Enable the TraceMonkey tracing JIT\n"
" -m Enable the JaegerMonkey method JIT\n" " -m Enable the JaegerMonkey method JIT\n"
" -a Always method JIT, ignore internal tuning\n"
" This only has effect with -m\n"
" -p Enable loop profiling for TraceMonkey\n" " -p Enable loop profiling for TraceMonkey\n"
" -d Enable debug mode\n" " -d Enable debug mode\n"
" -b Print timing statistics\n" " -b Print timing statistics\n"
@ -634,9 +636,10 @@ static const struct {
} js_options[] = { } js_options[] = {
{"anonfunfix", JSOPTION_ANONFUNFIX}, {"anonfunfix", JSOPTION_ANONFUNFIX},
{"atline", JSOPTION_ATLINE}, {"atline", JSOPTION_ATLINE},
{"jitprofiling", JSOPTION_PROFILING},
{"tracejit", JSOPTION_JIT}, {"tracejit", JSOPTION_JIT},
{"methodjit", JSOPTION_METHODJIT}, {"methodjit", JSOPTION_METHODJIT},
{"jitprofiling", JSOPTION_PROFILING}, {"methodjit_always",JSOPTION_METHODJIT_ALWAYS},
{"relimit", JSOPTION_RELIMIT}, {"relimit", JSOPTION_RELIMIT},
{"strict", JSOPTION_STRICT}, {"strict", JSOPTION_STRICT},
{"werror", JSOPTION_WERROR}, {"werror", JSOPTION_WERROR},
@ -807,6 +810,10 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
JS_ToggleOptions(cx, JSOPTION_METHODJIT); JS_ToggleOptions(cx, JSOPTION_METHODJIT);
break; break;
case 'a':
JS_ToggleOptions(cx, JSOPTION_METHODJIT_ALWAYS);
break;
case 'p': case 'p':
enableProfiling = !enableProfiling; enableProfiling = !enableProfiling;
JS_ToggleOptions(cx, JSOPTION_PROFILING); JS_ToggleOptions(cx, JSOPTION_PROFILING);

View File

@ -650,7 +650,8 @@ function optionsClear() {
if (optionName && if (optionName &&
optionName != "methodjit" && optionName != "methodjit" &&
optionName != "tracejit" && optionName != "tracejit" &&
optionName != "jitprofiling") optionName != "jitprofiling" &&
optionName != "methodjit_always")
{ {
options(optionName); options(optionName);
} }

View File

@ -33,5 +33,6 @@ user_pref("javascript.options.methodjit.chrome", false);
user_pref("javascript.options.methodjit.content", true); user_pref("javascript.options.methodjit.content", true);
user_pref("javascript.options.jitprofiling.chrome", false); user_pref("javascript.options.jitprofiling.chrome", false);
user_pref("javascript.options.jitprofiling.content", true); user_pref("javascript.options.jitprofiling.content", true);
user_pref("javascript.options.methodjit_always", false);
user_pref("javascript.options.strict", false); user_pref("javascript.options.strict", false);
user_pref("javascript.options.werror", false); user_pref("javascript.options.werror", false);

View File

@ -604,6 +604,7 @@ pref("javascript.options.methodjit.content", true);
pref("javascript.options.methodjit.chrome", false); pref("javascript.options.methodjit.chrome", false);
pref("javascript.options.jitprofiling.content", true); pref("javascript.options.jitprofiling.content", true);
pref("javascript.options.jitprofiling.chrome", false); pref("javascript.options.jitprofiling.chrome", false);
pref("javascript.options.methodjit_always", false);
// This preference limits the memory usage of javascript. // This preference limits the memory usage of javascript.
// If you want to change these values for your device, // If you want to change these values for your device,
// please find Bug 417052 comment 17 and Bug 456721 // please find Bug 417052 comment 17 and Bug 456721