mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
Bug 1541404 part 13 - Add some code to support entering the interpreter and triggering Baseline compilation from the interpreter. r=tcampbell
The script->incWarmUpCounter() calls are moved out of the CanEnterBaseline functions and into the callers. This makes it easier to reason about and prevents incrementing it multiple times for the different tiers/flags. baselineWarmUpThreshold should be renamed to baseline{Jit,Compiler}WarmUpThreshold, but that will happen later with other prefs-related changes. Differential Revision: https://phabricator.services.mozilla.com/D27321 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
4ef4fd39da
commit
34cabb31fa
@ -136,18 +136,22 @@ JitExecStatus jit::EnterBaselineAtBranch(JSContext* cx, InterpreterFrame* fp,
|
||||
jsbytecode* pc) {
|
||||
MOZ_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
|
||||
|
||||
BaselineScript* baseline = fp->script()->baselineScript();
|
||||
|
||||
EnterJitData data(cx);
|
||||
PCMappingSlotInfo slotInfo;
|
||||
data.jitcode = baseline->nativeCodeForPC(fp->script(), pc, &slotInfo);
|
||||
MOZ_ASSERT(slotInfo.isStackSynced());
|
||||
|
||||
// Skip debug breakpoint/trap handler, the interpreter already handled it
|
||||
// for the current op.
|
||||
if (fp->isDebuggee()) {
|
||||
MOZ_RELEASE_ASSERT(baseline->hasDebugInstrumentation());
|
||||
data.jitcode += MacroAssembler::ToggledCallSize(data.jitcode);
|
||||
if (fp->script()->hasBaselineScript()) {
|
||||
BaselineScript* baseline = fp->script()->baselineScript();
|
||||
PCMappingSlotInfo slotInfo;
|
||||
data.jitcode = baseline->nativeCodeForPC(fp->script(), pc, &slotInfo);
|
||||
MOZ_ASSERT(slotInfo.isStackSynced());
|
||||
|
||||
// Skip debug breakpoint/trap handler, the interpreter already handled it
|
||||
// for the current op.
|
||||
if (fp->isDebuggee()) {
|
||||
MOZ_RELEASE_ASSERT(baseline->hasDebugInstrumentation());
|
||||
data.jitcode += MacroAssembler::ToggledCallSize(data.jitcode);
|
||||
}
|
||||
} else {
|
||||
MOZ_CRASH("NYI: Interpreter executeOp code");
|
||||
}
|
||||
|
||||
// Note: keep this in sync with SetEnterJitData.
|
||||
@ -231,7 +235,7 @@ MethodStatus jit::BaselineCompile(JSContext* cx, JSScript* script,
|
||||
}
|
||||
|
||||
static MethodStatus CanEnterBaselineJIT(JSContext* cx, HandleScript script,
|
||||
InterpreterFrame* osrSourceFrame) {
|
||||
AbstractFramePtr osrSourceFrame) {
|
||||
MOZ_ASSERT(jit::IsBaselineEnabled(cx));
|
||||
|
||||
// Skip if the script has been disabled.
|
||||
@ -259,7 +263,7 @@ static MethodStatus CanEnterBaselineJIT(JSContext* cx, HandleScript script,
|
||||
// already compiled in baseline, execution jumps directly into baseline
|
||||
// code. This is incorrect as h's baseline script does not have debug
|
||||
// instrumentation.
|
||||
if (osrSourceFrame && osrSourceFrame->isDebuggee() &&
|
||||
if (osrSourceFrame && osrSourceFrame.isDebuggee() &&
|
||||
!Debugger::ensureExecutionObservabilityOfOsrFrame(cx, osrSourceFrame)) {
|
||||
return Method_Error;
|
||||
}
|
||||
@ -277,7 +281,7 @@ static MethodStatus CanEnterBaselineJIT(JSContext* cx, HandleScript script,
|
||||
}
|
||||
|
||||
// Check script warm-up counter.
|
||||
if (script->incWarmUpCounter() <= JitOptions.baselineWarmUpThreshold) {
|
||||
if (script->getWarmUpCount() <= JitOptions.baselineWarmUpThreshold) {
|
||||
return Method_Skipped;
|
||||
}
|
||||
|
||||
@ -295,10 +299,38 @@ static MethodStatus CanEnterBaselineJIT(JSContext* cx, HandleScript script,
|
||||
// script being a debuggee script, e.g., when performing
|
||||
// Debugger.Frame.prototype.eval.
|
||||
bool forceDebugInstrumentation =
|
||||
osrSourceFrame && osrSourceFrame->isDebuggee();
|
||||
osrSourceFrame && osrSourceFrame.isDebuggee();
|
||||
return BaselineCompile(cx, script, forceDebugInstrumentation);
|
||||
}
|
||||
|
||||
static MethodStatus CanEnterBaselineInterpreter(JSContext* cx,
|
||||
HandleScript script) {
|
||||
MOZ_ASSERT(jit::IsBaselineEnabled(cx));
|
||||
MOZ_ASSERT(JitOptions.baselineInterpreter);
|
||||
|
||||
if (script->types()) {
|
||||
return Method_Compiled;
|
||||
}
|
||||
|
||||
// Check script warm-up counter.
|
||||
if (script->getWarmUpCount() <=
|
||||
JitOptions.baselineInterpreterWarmUpThreshold) {
|
||||
return Method_Skipped;
|
||||
}
|
||||
|
||||
if (!cx->realm()->ensureJitRealmExists(cx)) {
|
||||
return Method_Error;
|
||||
}
|
||||
|
||||
AutoKeepTypeScripts keepTypes(cx);
|
||||
if (!script->ensureHasTypes(cx, keepTypes)) {
|
||||
return Method_Error;
|
||||
}
|
||||
|
||||
return Method_Compiled;
|
||||
}
|
||||
|
||||
template <BaselineTier Tier>
|
||||
MethodStatus jit::CanEnterBaselineAtBranch(JSContext* cx,
|
||||
InterpreterFrame* fp) {
|
||||
if (!CheckFrame(fp)) {
|
||||
@ -306,9 +338,23 @@ MethodStatus jit::CanEnterBaselineAtBranch(JSContext* cx,
|
||||
}
|
||||
|
||||
RootedScript script(cx, fp->script());
|
||||
return CanEnterBaselineJIT(cx, script, fp);
|
||||
switch (Tier) {
|
||||
case BaselineTier::Interpreter:
|
||||
return CanEnterBaselineInterpreter(cx, script);
|
||||
|
||||
case BaselineTier::Compiler:
|
||||
return CanEnterBaselineJIT(cx, script, fp);
|
||||
}
|
||||
|
||||
MOZ_CRASH("Unexpected tier");
|
||||
}
|
||||
|
||||
template MethodStatus jit::CanEnterBaselineAtBranch<BaselineTier::Interpreter>(
|
||||
JSContext* cx, InterpreterFrame* fp);
|
||||
template MethodStatus jit::CanEnterBaselineAtBranch<BaselineTier::Compiler>(
|
||||
JSContext* cx, InterpreterFrame* fp);
|
||||
|
||||
template <BaselineTier Tier>
|
||||
MethodStatus jit::CanEnterBaselineMethod(JSContext* cx, RunState& state) {
|
||||
if (state.isInvoke()) {
|
||||
InvokeState& invoke = *state.asInvoke();
|
||||
@ -325,8 +371,58 @@ MethodStatus jit::CanEnterBaselineMethod(JSContext* cx, RunState& state) {
|
||||
}
|
||||
|
||||
RootedScript script(cx, state.script());
|
||||
return CanEnterBaselineJIT(cx, script, /* osrSourceFrame = */ nullptr);
|
||||
};
|
||||
switch (Tier) {
|
||||
case BaselineTier::Interpreter:
|
||||
return CanEnterBaselineInterpreter(cx, script);
|
||||
|
||||
case BaselineTier::Compiler:
|
||||
return CanEnterBaselineJIT(cx, script,
|
||||
/* osrSourceFrame = */ NullFramePtr());
|
||||
}
|
||||
|
||||
MOZ_CRASH("Unexpected tier");
|
||||
}
|
||||
|
||||
template MethodStatus jit::CanEnterBaselineMethod<BaselineTier::Interpreter>(
|
||||
JSContext* cx, RunState& state);
|
||||
template MethodStatus jit::CanEnterBaselineMethod<BaselineTier::Compiler>(
|
||||
JSContext* cx, RunState& state);
|
||||
|
||||
bool jit::BaselineCompileFromBaselineInterpreter(JSContext* cx,
|
||||
BaselineFrame* frame,
|
||||
uint8_t** res) {
|
||||
MOZ_ASSERT(frame->runningInInterpreter());
|
||||
|
||||
RootedScript script(cx, frame->script());
|
||||
jsbytecode* pc = frame->interpreterPC();
|
||||
MOZ_ASSERT(pc == script->code() || *pc == JSOP_LOOPENTRY);
|
||||
|
||||
MethodStatus status = CanEnterBaselineJIT(cx, script,
|
||||
/* osrSourceFrame = */ frame);
|
||||
switch (status) {
|
||||
case Method_Error:
|
||||
return false;
|
||||
|
||||
case Method_CantCompile:
|
||||
case Method_Skipped:
|
||||
*res = nullptr;
|
||||
return true;
|
||||
|
||||
case Method_Compiled: {
|
||||
if (*pc == JSOP_LOOPENTRY) {
|
||||
PCMappingSlotInfo slotInfo;
|
||||
*res = script->baselineScript()->nativeCodeForPC(script, pc, &slotInfo);
|
||||
MOZ_ASSERT(slotInfo.isStackSynced());
|
||||
} else {
|
||||
*res = script->baselineScript()->warmUpCheckPrologueAddr();
|
||||
}
|
||||
frame->prepareForBaselineInterpreterToJitOSR();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_CRASH("Unexpected status");
|
||||
}
|
||||
|
||||
BaselineScript* BaselineScript::New(
|
||||
JSScript* jsscript, uint32_t bailoutPrologueOffset,
|
||||
|
@ -555,13 +555,23 @@ inline bool IsBaselineEnabled(JSContext* cx) {
|
||||
#endif
|
||||
}
|
||||
|
||||
enum class BaselineTier { Interpreter, Compiler };
|
||||
|
||||
template <BaselineTier Tier>
|
||||
MethodStatus CanEnterBaselineMethod(JSContext* cx, RunState& state);
|
||||
|
||||
template <BaselineTier Tier>
|
||||
MethodStatus CanEnterBaselineAtBranch(JSContext* cx, InterpreterFrame* fp);
|
||||
|
||||
JitExecStatus EnterBaselineAtBranch(JSContext* cx, InterpreterFrame* fp,
|
||||
jsbytecode* pc);
|
||||
|
||||
// Called by the Baseline Interpreter to compile a script for the Baseline JIT.
|
||||
// |res| is set to the native code address in the BaselineScript to jump to, or
|
||||
// nullptr if we were unable to compile this script.
|
||||
bool BaselineCompileFromBaselineInterpreter(JSContext* cx, BaselineFrame* frame,
|
||||
uint8_t** res);
|
||||
|
||||
void FinishDiscardBaselineScript(FreeOp* fop, JSScript* script);
|
||||
|
||||
void AddSizeOfBaselineData(JSScript* script, mozilla::MallocSizeOf mallocSizeOf,
|
||||
|
@ -2353,7 +2353,8 @@ MethodStatus jit::CanEnterIon(JSContext* cx, RunState& state) {
|
||||
// If --ion-eager is used, compile with Baseline first, so that we
|
||||
// can directly enter IonMonkey.
|
||||
if (JitOptions.eagerIonCompilation() && !script->hasBaselineScript()) {
|
||||
MethodStatus status = CanEnterBaselineMethod(cx, state);
|
||||
MethodStatus status =
|
||||
CanEnterBaselineMethod<BaselineTier::Compiler>(cx, state);
|
||||
if (status != Method_Compiled) {
|
||||
return status;
|
||||
}
|
||||
|
@ -138,6 +138,8 @@ EnterJitStatus js::jit::MaybeEnterJit(JSContext* cx, RunState& state) {
|
||||
break;
|
||||
}
|
||||
|
||||
script->incWarmUpCounter();
|
||||
|
||||
// Try to Ion-compile.
|
||||
if (jit::IsIonEnabled(cx)) {
|
||||
jit::MethodStatus status = jit::CanEnterIon(cx, state);
|
||||
@ -152,7 +154,8 @@ EnterJitStatus js::jit::MaybeEnterJit(JSContext* cx, RunState& state) {
|
||||
|
||||
// Try to Baseline-compile.
|
||||
if (jit::IsBaselineEnabled(cx)) {
|
||||
jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state);
|
||||
jit::MethodStatus status =
|
||||
jit::CanEnterBaselineMethod<BaselineTier::Compiler>(cx, state);
|
||||
if (status == jit::Method_Error) {
|
||||
return EnterJitStatus::Error;
|
||||
}
|
||||
@ -160,6 +163,18 @@ EnterJitStatus js::jit::MaybeEnterJit(JSContext* cx, RunState& state) {
|
||||
code = script->jitCodeRaw();
|
||||
break;
|
||||
}
|
||||
|
||||
if (JitOptions.baselineInterpreter) {
|
||||
jit::MethodStatus status =
|
||||
jit::CanEnterBaselineMethod<BaselineTier::Interpreter>(cx, state);
|
||||
if (status == jit::Method_Error) {
|
||||
return EnterJitStatus::Error;
|
||||
}
|
||||
if (status == jit::Method_Compiled) {
|
||||
code = script->jitCodeRaw();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EnterJitStatus::NotEntered;
|
||||
|
@ -140,6 +140,9 @@ DefaultJitOptions::DefaultJitOptions() {
|
||||
// disabled.
|
||||
SET_DEFAULT(disableOptimizationLevels, false);
|
||||
|
||||
// Whether the Baseline Interpreter is enabled.
|
||||
SET_DEFAULT(baselineInterpreter, false);
|
||||
|
||||
// Whether IonBuilder should prefer IC generation above specialized MIR.
|
||||
SET_DEFAULT(forceInlineCaches, false);
|
||||
|
||||
@ -152,6 +155,10 @@ DefaultJitOptions::DefaultJitOptions() {
|
||||
// Whether to enable extra code to perform dynamic validations.
|
||||
SET_DEFAULT(runExtraChecks, false);
|
||||
|
||||
// How many invocations or loop iterations are needed before functions
|
||||
// enter the Baseline Interpreter.
|
||||
SET_DEFAULT(baselineInterpreterWarmUpThreshold, 10);
|
||||
|
||||
// How many invocations or loop iterations are needed before functions
|
||||
// are compiled with the baseline compiler.
|
||||
// Duplicated in all.js - ensure both match.
|
||||
|
@ -66,6 +66,7 @@ struct DefaultJitOptions {
|
||||
bool disableSincos;
|
||||
bool disableSink;
|
||||
bool disableOptimizationLevels;
|
||||
bool baselineInterpreter;
|
||||
bool forceInlineCaches;
|
||||
bool fullDebugChecks;
|
||||
bool limitScriptSize;
|
||||
@ -82,6 +83,7 @@ struct DefaultJitOptions {
|
||||
bool enableWasmImportCallSpew;
|
||||
bool enableWasmFuncCallSpew;
|
||||
#endif
|
||||
uint32_t baselineInterpreterWarmUpThreshold;
|
||||
uint32_t baselineWarmUpThreshold;
|
||||
uint32_t normalIonWarmUpThreshold;
|
||||
uint32_t fullIonWarmUpThreshold;
|
||||
|
@ -3034,14 +3034,14 @@ bool Debugger::ensureExecutionObservabilityOfScript(JSContext* cx,
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool Debugger::ensureExecutionObservabilityOfOsrFrame(JSContext* cx,
|
||||
InterpreterFrame* frame) {
|
||||
MOZ_ASSERT(frame->isDebuggee());
|
||||
if (frame->script()->hasBaselineScript() &&
|
||||
frame->script()->baselineScript()->hasDebugInstrumentation()) {
|
||||
bool Debugger::ensureExecutionObservabilityOfOsrFrame(
|
||||
JSContext* cx, AbstractFramePtr osrSourceFrame) {
|
||||
MOZ_ASSERT(osrSourceFrame.isDebuggee());
|
||||
if (osrSourceFrame.script()->hasBaselineScript() &&
|
||||
osrSourceFrame.script()->baselineScript()->hasDebugInstrumentation()) {
|
||||
return true;
|
||||
}
|
||||
ExecutionObservableFrame obs(frame);
|
||||
ExecutionObservableFrame obs(osrSourceFrame);
|
||||
return updateExecutionObservabilityOfFrames(cx, obs, Observing);
|
||||
}
|
||||
|
||||
|
@ -818,7 +818,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger> {
|
||||
|
||||
public:
|
||||
static MOZ_MUST_USE bool ensureExecutionObservabilityOfOsrFrame(
|
||||
JSContext* cx, InterpreterFrame* frame);
|
||||
JSContext* cx, AbstractFramePtr osrSourceFrame);
|
||||
|
||||
// Public for DebuggerScript_setBreakpoint.
|
||||
static MOZ_MUST_USE bool ensureExecutionObservabilityOfScript(
|
||||
|
@ -1966,7 +1966,14 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
|
||||
COUNT_COVERAGE();
|
||||
// Attempt on-stack replacement with Baseline code.
|
||||
if (jit::IsBaselineEnabled(cx)) {
|
||||
jit::MethodStatus status = jit::CanEnterBaselineAtBranch(cx, REGS.fp());
|
||||
script->incWarmUpCounter();
|
||||
|
||||
using Tier = jit::BaselineTier;
|
||||
jit::MethodStatus status =
|
||||
jit::JitOptions.baselineInterpreter
|
||||
? jit::CanEnterBaselineAtBranch<Tier::Interpreter>(cx,
|
||||
REGS.fp())
|
||||
: jit::CanEnterBaselineAtBranch<Tier::Compiler>(cx, REGS.fp());
|
||||
if (status == jit::Method_Error) {
|
||||
goto error;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user