зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1214059: Baseline: Enable switch to debug mode at function entry, r=jandem
This commit is contained in:
Родитель
9eb5f1349b
Коммит
63117a51eb
|
@ -702,6 +702,10 @@ BaselineCompiler::emitInterruptCheck()
|
|||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*IonCompileScriptForBaselineFn)(JSContext*, BaselineFrame*, jsbytecode*);
|
||||
static const VMFunction IonCompileScriptForBaselineInfo =
|
||||
FunctionInfo<IonCompileScriptForBaselineFn>(IonCompileScriptForBaseline);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emitWarmUpCounterIncrement(bool allowOsr)
|
||||
{
|
||||
|
@ -711,6 +715,8 @@ BaselineCompiler::emitWarmUpCounterIncrement(bool allowOsr)
|
|||
if (!ionCompileable_ && !ionOSRCompileable_)
|
||||
return true;
|
||||
|
||||
frame.assertSyncedStack();
|
||||
|
||||
Register scriptReg = R2.scratchReg();
|
||||
Register countReg = R0.scratchReg();
|
||||
Address warmUpCounterAddr(scriptReg, JSScript::offsetOfWarmUpCounter());
|
||||
|
@ -743,11 +749,28 @@ BaselineCompiler::emitWarmUpCounterIncrement(bool allowOsr)
|
|||
Address(scriptReg, JSScript::offsetOfIonScript()),
|
||||
ImmPtr(ION_COMPILING_SCRIPT), &skipCall);
|
||||
|
||||
// Call IC.
|
||||
ICWarmUpCounter_Fallback::Compiler stubCompiler(cx);
|
||||
if (!emitNonOpIC(stubCompiler.getStub(&stubSpace_)))
|
||||
return false;
|
||||
// Try to compile and/or finish a compilation.
|
||||
if (JSOp(*pc) == JSOP_LOOPENTRY) {
|
||||
// During the loop entry we can try to OSR into ion.
|
||||
// The ic has logic for this.
|
||||
ICWarmUpCounter_Fallback::Compiler stubCompiler(cx);
|
||||
if (!emitNonOpIC(stubCompiler.getStub(&stubSpace_)))
|
||||
return false;
|
||||
} else {
|
||||
// To call stubs we need to have an opcode. This code handles the
|
||||
// prologue and there is no dedicatd opcode present. Therefore use an
|
||||
// annotated vm call.
|
||||
prepareVMCall();
|
||||
|
||||
masm.Push(ImmPtr(pc));
|
||||
masm.PushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
|
||||
if (!callVM(IonCompileScriptForBaselineInfo))
|
||||
return false;
|
||||
|
||||
// Annotate the ICEntry as warmup counter.
|
||||
icEntries_.back().setFakeKind(ICEntry::Kind_WarmupCounter);
|
||||
}
|
||||
masm.bind(&skipCall);
|
||||
|
||||
return true;
|
||||
|
|
|
@ -101,6 +101,7 @@ struct DebugModeOSREntry
|
|||
|
||||
bool needsRecompileInfo() const {
|
||||
return frameKind == ICEntry::Kind_CallVM ||
|
||||
frameKind == ICEntry::Kind_WarmupCounter ||
|
||||
frameKind == ICEntry::Kind_StackCheck ||
|
||||
frameKind == ICEntry::Kind_EarlyStackCheck ||
|
||||
frameKind == ICEntry::Kind_DebugTrap ||
|
||||
|
@ -298,6 +299,8 @@ ICEntryKindToString(ICEntry::Kind kind)
|
|||
return "non-op IC";
|
||||
case ICEntry::Kind_CallVM:
|
||||
return "callVM";
|
||||
case ICEntry::Kind_WarmupCounter:
|
||||
return "warmup counter";
|
||||
case ICEntry::Kind_StackCheck:
|
||||
return "stack check";
|
||||
case ICEntry::Kind_EarlyStackCheck:
|
||||
|
@ -359,6 +362,7 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const Debugger::ExecutionObservab
|
|||
// B. From a VM call.
|
||||
// H. From inside HandleExceptionBaseline.
|
||||
// I. From inside the interrupt handler via the prologue stack check.
|
||||
// J. From the warmup counter in the prologue.
|
||||
//
|
||||
// On to Off:
|
||||
// - All the ways above.
|
||||
|
@ -367,10 +371,10 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const Debugger::ExecutionObservab
|
|||
// E. From the debug epilogue.
|
||||
//
|
||||
// Off to On to Off:
|
||||
// F. Undo case B or I above on previously patched yet unpopped frames.
|
||||
// F. Undo case B, I or J above on previously patched yet unpopped frames.
|
||||
//
|
||||
// On to Off to On:
|
||||
// G. Undo cases B, C, D, E, or I above on previously patched yet unpopped
|
||||
// G. Undo cases B, C, D, E, I or J above on previously patched yet unpopped
|
||||
// frames.
|
||||
//
|
||||
// In general, we patch the return address from the VM call to return to a
|
||||
|
@ -456,7 +460,7 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const Debugger::ExecutionObservab
|
|||
|
||||
// Cases F and G above.
|
||||
//
|
||||
// We undo a previous recompile by handling cases B, C, D, E, or I
|
||||
// We undo a previous recompile by handling cases B, C, D, E, I or J
|
||||
// like normal, except that we retrieve the pc information via
|
||||
// the previous OSR debug info stashed on the frame.
|
||||
BaselineDebugModeOSRInfo* info = iter.baselineFrame()->getDebugModeOSRInfo();
|
||||
|
@ -464,17 +468,19 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const Debugger::ExecutionObservab
|
|||
MOZ_ASSERT(info->pc == pc);
|
||||
MOZ_ASSERT(info->frameKind == kind);
|
||||
|
||||
// Case G, might need to undo B, C, D, E, or I.
|
||||
// Case G, might need to undo B, C, D, E, I or J.
|
||||
MOZ_ASSERT_IF(script->baselineScript()->hasDebugInstrumentation(),
|
||||
kind == ICEntry::Kind_CallVM ||
|
||||
kind == ICEntry::Kind_WarmupCounter ||
|
||||
kind == ICEntry::Kind_StackCheck ||
|
||||
kind == ICEntry::Kind_EarlyStackCheck ||
|
||||
kind == ICEntry::Kind_DebugTrap ||
|
||||
kind == ICEntry::Kind_DebugPrologue ||
|
||||
kind == ICEntry::Kind_DebugEpilogue);
|
||||
// Case F, should only need to undo case B or I.
|
||||
// Case F, should only need to undo case B, I or J.
|
||||
MOZ_ASSERT_IF(!script->baselineScript()->hasDebugInstrumentation(),
|
||||
kind == ICEntry::Kind_CallVM ||
|
||||
kind == ICEntry::Kind_WarmupCounter||
|
||||
kind == ICEntry::Kind_StackCheck ||
|
||||
kind == ICEntry::Kind_EarlyStackCheck);
|
||||
|
||||
|
@ -505,6 +511,18 @@ PatchBaselineFramesForDebugMode(JSContext* cx, const Debugger::ExecutionObservab
|
|||
break;
|
||||
}
|
||||
|
||||
case ICEntry::Kind_WarmupCounter: {
|
||||
// Case J above.
|
||||
//
|
||||
// Patching mechanism is identical to a CallVM. This is
|
||||
// handled especially only because the warmup counter VM call is
|
||||
// part of the prologue, and not tied an opcode.
|
||||
ICEntry& warmupCountEntry = bl->warmupCountICEntry();
|
||||
recompInfo->resumeAddr = bl->returnAddressForIC(warmupCountEntry);
|
||||
popFrameReg = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case ICEntry::Kind_StackCheck:
|
||||
case ICEntry::Kind_EarlyStackCheck: {
|
||||
// Case I above.
|
||||
|
@ -956,6 +974,7 @@ IsReturningFromCallVM(BaselineDebugModeOSRInfo* info)
|
|||
// The stack check entries are returns from a callVM, but have a special
|
||||
// kind because they do not exist in a 1-1 relationship with a pc offset.
|
||||
return info->frameKind == ICEntry::Kind_CallVM ||
|
||||
info->frameKind == ICEntry::Kind_WarmupCounter ||
|
||||
info->frameKind == ICEntry::Kind_StackCheck ||
|
||||
info->frameKind == ICEntry::Kind_EarlyStackCheck;
|
||||
}
|
||||
|
@ -973,6 +992,7 @@ EmitBranchIsReturningFromCallVM(MacroAssembler& masm, Register entry, Label* lab
|
|||
{
|
||||
// Keep this in sync with IsReturningFromCallVM.
|
||||
EmitBranchICEntryKind(masm, entry, ICEntry::Kind_CallVM, label);
|
||||
EmitBranchICEntryKind(masm, entry, ICEntry::Kind_WarmupCounter, label);
|
||||
EmitBranchICEntryKind(masm, entry, ICEntry::Kind_StackCheck, label);
|
||||
EmitBranchICEntryKind(masm, entry, ICEntry::Kind_EarlyStackCheck, label);
|
||||
}
|
||||
|
|
|
@ -50,66 +50,6 @@ namespace jit {
|
|||
// WarmUpCounter_Fallback
|
||||
//
|
||||
|
||||
static bool
|
||||
EnsureCanEnterIon(JSContext* cx, ICWarmUpCounter_Fallback* stub, BaselineFrame* frame,
|
||||
HandleScript script, jsbytecode* pc, void** jitcodePtr)
|
||||
{
|
||||
MOZ_ASSERT(jitcodePtr);
|
||||
MOZ_ASSERT(!*jitcodePtr);
|
||||
|
||||
bool isLoopEntry = (JSOp(*pc) == JSOP_LOOPENTRY);
|
||||
|
||||
MethodStatus stat;
|
||||
if (isLoopEntry) {
|
||||
MOZ_ASSERT(LoopEntryCanIonOsr(pc));
|
||||
JitSpew(JitSpew_BaselineOSR, " Compile at loop entry!");
|
||||
stat = CanEnterAtBranch(cx, script, frame, pc);
|
||||
} else if (frame->isFunctionFrame()) {
|
||||
JitSpew(JitSpew_BaselineOSR, " Compile function from top for later entry!");
|
||||
stat = CompileFunctionForBaseline(cx, script, frame);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (stat == Method_Error) {
|
||||
JitSpew(JitSpew_BaselineOSR, " Compile with Ion errored!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stat == Method_CantCompile)
|
||||
JitSpew(JitSpew_BaselineOSR, " Can't compile with Ion!");
|
||||
else if (stat == Method_Skipped)
|
||||
JitSpew(JitSpew_BaselineOSR, " Skipped compile with Ion!");
|
||||
else if (stat == Method_Compiled)
|
||||
JitSpew(JitSpew_BaselineOSR, " Compiled with Ion!");
|
||||
else
|
||||
MOZ_CRASH("Invalid MethodStatus!");
|
||||
|
||||
// Failed to compile. Reset warm-up counter and return.
|
||||
if (stat != Method_Compiled) {
|
||||
// TODO: If stat == Method_CantCompile, insert stub that just skips the
|
||||
// warm-up counter entirely, instead of resetting it.
|
||||
bool bailoutExpected = script->hasIonScript() && script->ionScript()->bailoutExpected();
|
||||
if (stat == Method_CantCompile || bailoutExpected) {
|
||||
JitSpew(JitSpew_BaselineOSR, " Reset WarmUpCounter cantCompile=%s bailoutExpected=%s!",
|
||||
stat == Method_CantCompile ? "yes" : "no",
|
||||
bailoutExpected ? "yes" : "no");
|
||||
script->resetWarmUpCounter();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isLoopEntry) {
|
||||
IonScript* ion = script->ionScript();
|
||||
MOZ_ASSERT(cx->runtime()->spsProfiler.enabled() == ion->hasProfilingInstrumentation());
|
||||
MOZ_ASSERT(ion->osrPc() == pc);
|
||||
|
||||
JitSpew(JitSpew_BaselineOSR, " OSR possible!");
|
||||
*jitcodePtr = ion->method()->raw() + ion->osrEntryOffset();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// The following data is kept in a temporary heap-allocated buffer, stored in
|
||||
|
@ -184,56 +124,33 @@ PrepareOsrTempData(JSContext* cx, ICWarmUpCounter_Fallback* stub, BaselineFrame*
|
|||
}
|
||||
|
||||
static bool
|
||||
DoWarmUpCounterFallback(JSContext* cx, BaselineFrame* frame, ICWarmUpCounter_Fallback* stub,
|
||||
IonOsrTempData** infoPtr)
|
||||
DoWarmUpCounterFallbackOSR(JSContext* cx, BaselineFrame* frame, ICWarmUpCounter_Fallback* stub,
|
||||
IonOsrTempData** infoPtr)
|
||||
{
|
||||
MOZ_ASSERT(infoPtr);
|
||||
*infoPtr = nullptr;
|
||||
|
||||
// A TI OOM will disable TI and Ion.
|
||||
if (!jit::IsIonEnabled(cx))
|
||||
return true;
|
||||
|
||||
RootedScript script(cx, frame->script());
|
||||
jsbytecode* pc = stub->icEntry()->pc(script);
|
||||
bool isLoopEntry = JSOp(*pc) == JSOP_LOOPENTRY;
|
||||
MOZ_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
|
||||
|
||||
MOZ_ASSERT(!isLoopEntry || LoopEntryCanIonOsr(pc));
|
||||
FallbackICSpew(cx, stub, "WarmUpCounter(%d)", int(script->pcToOffset(pc)));
|
||||
|
||||
FallbackICSpew(cx, stub, "WarmUpCounter(%d)", isLoopEntry ? int(script->pcToOffset(pc)) : int(-1));
|
||||
|
||||
if (!script->canIonCompile()) {
|
||||
// TODO: ASSERT that ion-compilation-disabled checker stub doesn't exist.
|
||||
// TODO: Clear all optimized stubs.
|
||||
// TODO: Add a ion-compilation-disabled checker IC stub
|
||||
script->resetWarmUpCounter();
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!script->isIonCompilingOffThread());
|
||||
|
||||
// If Ion script exists, but PC is not at a loop entry, then Ion will be entered for
|
||||
// this script at an appropriate LOOPENTRY or the next time this function is called.
|
||||
if (script->hasIonScript() && !isLoopEntry) {
|
||||
JitSpew(JitSpew_BaselineOSR, "IonScript exists, but not at loop entry!");
|
||||
// TODO: ASSERT that a ion-script-already-exists checker stub doesn't exist.
|
||||
// TODO: Clear all optimized stubs.
|
||||
// TODO: Add a ion-script-already-exists checker stub.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ensure that Ion-compiled code is available.
|
||||
JitSpew(JitSpew_BaselineOSR,
|
||||
"WarmUpCounter for %s:%" PRIuSIZE " reached %d at pc %p, trying to switch to Ion!",
|
||||
script->filename(), script->lineno(), (int) script->getWarmUpCount(), (void*) pc);
|
||||
void* jitcode = nullptr;
|
||||
if (!EnsureCanEnterIon(cx, stub, frame, script, pc, &jitcode))
|
||||
if (!IonCompileScriptForBaseline(cx, frame, pc))
|
||||
return false;
|
||||
|
||||
// Jitcode should only be set here if not at loop entry.
|
||||
MOZ_ASSERT_IF(!isLoopEntry, !jitcode);
|
||||
if (!jitcode)
|
||||
if (!script->hasIonScript() || script->ionScript()->osrPc() != pc ||
|
||||
script->ionScript()->bailoutExpected())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
IonScript* ion = script->ionScript();
|
||||
MOZ_ASSERT(cx->runtime()->spsProfiler.enabled() == ion->hasProfilingInstrumentation());
|
||||
MOZ_ASSERT(ion->osrPc() == pc);
|
||||
|
||||
JitSpew(JitSpew_BaselineOSR, " OSR possible!");
|
||||
void* jitcode = ion->method()->raw() + ion->osrEntryOffset();
|
||||
|
||||
// Prepare the temporary heap copy of the fake InterpreterFrame and actual args list.
|
||||
JitSpew(JitSpew_BaselineOSR, "Got jitcode. Preparing for OSR into ion.");
|
||||
|
@ -245,10 +162,10 @@ DoWarmUpCounterFallback(JSContext* cx, BaselineFrame* frame, ICWarmUpCounter_Fal
|
|||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*DoWarmUpCounterFallbackFn)(JSContext*, BaselineFrame*,
|
||||
ICWarmUpCounter_Fallback*, IonOsrTempData** infoPtr);
|
||||
static const VMFunction DoWarmUpCounterFallbackInfo =
|
||||
FunctionInfo<DoWarmUpCounterFallbackFn>(DoWarmUpCounterFallback);
|
||||
typedef bool (*DoWarmUpCounterFallbackOSRFn)(JSContext*, BaselineFrame*,
|
||||
ICWarmUpCounter_Fallback*, IonOsrTempData** infoPtr);
|
||||
static const VMFunction DoWarmUpCounterFallbackOSRInfo =
|
||||
FunctionInfo<DoWarmUpCounterFallbackOSRFn>(DoWarmUpCounterFallbackOSR);
|
||||
|
||||
bool
|
||||
ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
|
@ -259,7 +176,7 @@ ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
enterStubFrame(masm, R1.scratchReg());
|
||||
|
||||
Label noCompiledCode;
|
||||
// Call DoWarmUpCounterFallback to compile/check-for Ion-compiled function
|
||||
// Call DoWarmUpCounterFallbackOSR to compile/check-for Ion-compiled function
|
||||
{
|
||||
// Push IonOsrTempData pointer storage
|
||||
masm.subFromStackPtr(Imm32(sizeof(void*)));
|
||||
|
@ -270,7 +187,7 @@ ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
|
||||
pushStubPayload(masm, R0.scratchReg());
|
||||
|
||||
if (!callVM(DoWarmUpCounterFallbackInfo, masm))
|
||||
if (!callVM(DoWarmUpCounterFallbackOSRInfo, masm))
|
||||
return false;
|
||||
|
||||
// Pop IonOsrTempData pointer.
|
||||
|
|
|
@ -3467,6 +3467,8 @@ IsCacheableDOMProxy(JSObject* obj)
|
|||
return handler->family() == GetDOMProxyHandlerFamily();
|
||||
}
|
||||
|
||||
struct IonOsrTempData;
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -697,6 +697,18 @@ BaselineScript::stackCheckICEntry(bool earlyCheck)
|
|||
MOZ_CRASH("No stack check ICEntry found.");
|
||||
}
|
||||
|
||||
ICEntry&
|
||||
BaselineScript::warmupCountICEntry()
|
||||
{
|
||||
// The stack check will be at a very low offset, so just do a linear search
|
||||
// from the beginning.
|
||||
for (size_t i = 0; i < numICEntries() && icEntry(i).pcOffset() == 0; i++) {
|
||||
if (icEntry(i).kind() == ICEntry::Kind_WarmupCounter)
|
||||
return icEntry(i);
|
||||
}
|
||||
MOZ_CRASH("No warmup count ICEntry found.");
|
||||
}
|
||||
|
||||
ICEntry&
|
||||
BaselineScript::icEntryFromReturnAddress(uint8_t* returnAddr)
|
||||
{
|
||||
|
|
|
@ -370,6 +370,7 @@ struct BaselineScript
|
|||
ICEntry& icEntryFromPCOffset(uint32_t pcOffset, ICEntry* prevLookedUpEntry);
|
||||
ICEntry& callVMEntryFromPCOffset(uint32_t pcOffset);
|
||||
ICEntry& stackCheckICEntry(bool earlyCheck);
|
||||
ICEntry& warmupCountICEntry();
|
||||
ICEntry& icEntryFromReturnAddress(uint8_t* returnAddr);
|
||||
uint8_t* returnAddressForIC(const ICEntry& ent);
|
||||
|
||||
|
|
|
@ -2483,76 +2483,6 @@ jit::OffThreadCompilationAvailable(JSContext* cx)
|
|||
&& CanUseExtraThreads();
|
||||
}
|
||||
|
||||
// Decide if a transition from interpreter execution to Ion code should occur.
|
||||
// May compile or recompile the target JSScript.
|
||||
MethodStatus
|
||||
jit::CanEnterAtBranch(JSContext* cx, HandleScript script, BaselineFrame* osrFrame, jsbytecode* pc)
|
||||
{
|
||||
MOZ_ASSERT(jit::IsIonEnabled(cx));
|
||||
MOZ_ASSERT((JSOp)*pc == JSOP_LOOPENTRY);
|
||||
MOZ_ASSERT(LoopEntryCanIonOsr(pc));
|
||||
|
||||
// Skip if the script has been disabled.
|
||||
if (!script->canIonCompile())
|
||||
return Method_Skipped;
|
||||
|
||||
// Skip if the script is being compiled off thread.
|
||||
if (script->isIonCompilingOffThread())
|
||||
return Method_Skipped;
|
||||
|
||||
// Skip if the code is expected to result in a bailout.
|
||||
if (script->hasIonScript() && script->ionScript()->bailoutExpected())
|
||||
return Method_Skipped;
|
||||
|
||||
// Optionally ignore on user request.
|
||||
if (!JitOptions.osr)
|
||||
return Method_Skipped;
|
||||
|
||||
// Mark as forbidden if frame can't be handled.
|
||||
if (!CheckFrame(cx, osrFrame)) {
|
||||
ForbidCompilation(cx, script);
|
||||
return Method_CantCompile;
|
||||
}
|
||||
|
||||
// Check if the jitcode still needs to get linked and do this
|
||||
// to have a valid IonScript.
|
||||
if (script->baselineScript()->hasPendingIonBuilder())
|
||||
LazyLink(cx, script);
|
||||
|
||||
// By default a recompilation doesn't happen on osr mismatch.
|
||||
// Decide if we want to force a recompilation if this happens too much.
|
||||
bool force = false;
|
||||
if (script->hasIonScript() && pc != script->ionScript()->osrPc()) {
|
||||
uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
|
||||
if (count <= JitOptions.osrPcMismatchesBeforeRecompile)
|
||||
return Method_Skipped;
|
||||
force = true;
|
||||
}
|
||||
|
||||
// Attempt compilation.
|
||||
// - Returns Method_Compiled if the right ionscript is present
|
||||
// (Meaning it was present or a sequantial compile finished)
|
||||
// - Returns Method_Skipped if pc doesn't match
|
||||
// (This means a background thread compilation with that pc could have started or not.)
|
||||
RootedScript rscript(cx, script);
|
||||
MethodStatus status = Compile(cx, rscript, osrFrame, pc, osrFrame->isConstructing(),
|
||||
force);
|
||||
if (status != Method_Compiled) {
|
||||
if (status == Method_CantCompile)
|
||||
ForbidCompilation(cx, script);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Return the compilation was skipped when the osr pc wasn't adjusted.
|
||||
// This can happen when there was still an IonScript available and a
|
||||
// background compilation started, but hasn't finished yet.
|
||||
// Or when we didn't force a recompile.
|
||||
if (script->hasIonScript() && pc != script->ionScript()->osrPc())
|
||||
return Method_Skipped;
|
||||
|
||||
return Method_Compiled;
|
||||
}
|
||||
|
||||
MethodStatus
|
||||
jit::CanEnter(JSContext* cx, RunState& state)
|
||||
{
|
||||
|
@ -2627,8 +2557,8 @@ jit::CanEnter(JSContext* cx, RunState& state)
|
|||
return Method_Compiled;
|
||||
}
|
||||
|
||||
MethodStatus
|
||||
jit::CompileFunctionForBaseline(JSContext* cx, HandleScript script, BaselineFrame* frame)
|
||||
static MethodStatus
|
||||
BaselineCanEnterAtEntry(JSContext* cx, HandleScript script, BaselineFrame* frame)
|
||||
{
|
||||
MOZ_ASSERT(jit::IsIonEnabled(cx));
|
||||
MOZ_ASSERT(frame->callee()->nonLazyScript()->canIonCompile());
|
||||
|
@ -2653,6 +2583,157 @@ jit::CompileFunctionForBaseline(JSContext* cx, HandleScript script, BaselineFram
|
|||
return Method_Compiled;
|
||||
}
|
||||
|
||||
// Decide if a transition from baseline execution to Ion code should occur.
|
||||
// May compile or recompile the target JSScript.
|
||||
static MethodStatus
|
||||
BaselineCanEnterAtBranch(JSContext* cx, HandleScript script, BaselineFrame* osrFrame, jsbytecode* pc)
|
||||
{
|
||||
MOZ_ASSERT(jit::IsIonEnabled(cx));
|
||||
MOZ_ASSERT((JSOp)*pc == JSOP_LOOPENTRY);
|
||||
MOZ_ASSERT(LoopEntryCanIonOsr(pc));
|
||||
|
||||
// Skip if the script has been disabled.
|
||||
if (!script->canIonCompile())
|
||||
return Method_Skipped;
|
||||
|
||||
// Skip if the script is being compiled off thread.
|
||||
if (script->isIonCompilingOffThread())
|
||||
return Method_Skipped;
|
||||
|
||||
// Skip if the code is expected to result in a bailout.
|
||||
if (script->hasIonScript() && script->ionScript()->bailoutExpected())
|
||||
return Method_Skipped;
|
||||
|
||||
// Optionally ignore on user request.
|
||||
if (!JitOptions.osr)
|
||||
return Method_Skipped;
|
||||
|
||||
// Mark as forbidden if frame can't be handled.
|
||||
if (!CheckFrame(cx, osrFrame)) {
|
||||
ForbidCompilation(cx, script);
|
||||
return Method_CantCompile;
|
||||
}
|
||||
|
||||
// Check if the jitcode still needs to get linked and do this
|
||||
// to have a valid IonScript.
|
||||
if (script->baselineScript()->hasPendingIonBuilder())
|
||||
LazyLink(cx, script);
|
||||
|
||||
// By default a recompilation doesn't happen on osr mismatch.
|
||||
// Decide if we want to force a recompilation if this happens too much.
|
||||
bool force = false;
|
||||
if (script->hasIonScript() && pc != script->ionScript()->osrPc()) {
|
||||
uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
|
||||
if (count <= JitOptions.osrPcMismatchesBeforeRecompile)
|
||||
return Method_Skipped;
|
||||
force = true;
|
||||
}
|
||||
|
||||
// Attempt compilation.
|
||||
// - Returns Method_Compiled if the right ionscript is present
|
||||
// (Meaning it was present or a sequantial compile finished)
|
||||
// - Returns Method_Skipped if pc doesn't match
|
||||
// (This means a background thread compilation with that pc could have started or not.)
|
||||
RootedScript rscript(cx, script);
|
||||
MethodStatus status = Compile(cx, rscript, osrFrame, pc, osrFrame->isConstructing(),
|
||||
force);
|
||||
if (status != Method_Compiled) {
|
||||
if (status == Method_CantCompile)
|
||||
ForbidCompilation(cx, script);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Return the compilation was skipped when the osr pc wasn't adjusted.
|
||||
// This can happen when there was still an IonScript available and a
|
||||
// background compilation started, but hasn't finished yet.
|
||||
// Or when we didn't force a recompile.
|
||||
if (script->hasIonScript() && pc != script->ionScript()->osrPc())
|
||||
return Method_Skipped;
|
||||
|
||||
return Method_Compiled;
|
||||
}
|
||||
|
||||
bool
|
||||
jit::IonCompileScriptForBaseline(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
|
||||
{
|
||||
// A TI OOM will disable TI and Ion.
|
||||
if (!jit::IsIonEnabled(cx))
|
||||
return true;
|
||||
|
||||
RootedScript script(cx, frame->script());
|
||||
bool isLoopEntry = JSOp(*pc) == JSOP_LOOPENTRY;
|
||||
|
||||
MOZ_ASSERT(!isLoopEntry || LoopEntryCanIonOsr(pc));
|
||||
|
||||
if (!script->canIonCompile()) {
|
||||
// TODO: ASSERT that ion-compilation-disabled checker stub doesn't exist.
|
||||
// TODO: Clear all optimized stubs.
|
||||
// TODO: Add a ion-compilation-disabled checker IC stub
|
||||
script->resetWarmUpCounter();
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!script->isIonCompilingOffThread());
|
||||
|
||||
// If Ion script exists, but PC is not at a loop entry, then Ion will be entered for
|
||||
// this script at an appropriate LOOPENTRY or the next time this function is called.
|
||||
if (script->hasIonScript() && !isLoopEntry) {
|
||||
JitSpew(JitSpew_BaselineOSR, "IonScript exists, but not at loop entry!");
|
||||
// TODO: ASSERT that a ion-script-already-exists checker stub doesn't exist.
|
||||
// TODO: Clear all optimized stubs.
|
||||
// TODO: Add a ion-script-already-exists checker stub.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ensure that Ion-compiled code is available.
|
||||
JitSpew(JitSpew_BaselineOSR,
|
||||
"WarmUpCounter for %s:%" PRIuSIZE " reached %d at pc %p, trying to switch to Ion!",
|
||||
script->filename(), script->lineno(), (int) script->getWarmUpCount(), (void*) pc);
|
||||
|
||||
MethodStatus stat;
|
||||
if (isLoopEntry) {
|
||||
MOZ_ASSERT(LoopEntryCanIonOsr(pc));
|
||||
JitSpew(JitSpew_BaselineOSR, " Compile at loop entry!");
|
||||
stat = BaselineCanEnterAtBranch(cx, script, frame, pc);
|
||||
} else if (frame->isFunctionFrame()) {
|
||||
JitSpew(JitSpew_BaselineOSR, " Compile function from top for later entry!");
|
||||
stat = BaselineCanEnterAtEntry(cx, script, frame);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (stat == Method_Error) {
|
||||
JitSpew(JitSpew_BaselineOSR, " Compile with Ion errored!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stat == Method_CantCompile)
|
||||
JitSpew(JitSpew_BaselineOSR, " Can't compile with Ion!");
|
||||
else if (stat == Method_Skipped)
|
||||
JitSpew(JitSpew_BaselineOSR, " Skipped compile with Ion!");
|
||||
else if (stat == Method_Compiled)
|
||||
JitSpew(JitSpew_BaselineOSR, " Compiled with Ion!");
|
||||
else
|
||||
MOZ_CRASH("Invalid MethodStatus!");
|
||||
|
||||
// Failed to compile. Reset warm-up counter and return.
|
||||
if (stat != Method_Compiled) {
|
||||
// TODO: If stat == Method_CantCompile, insert stub that just skips the
|
||||
// warm-up counter entirely, instead of resetting it.
|
||||
bool bailoutExpected = script->hasIonScript() && script->ionScript()->bailoutExpected();
|
||||
if (stat == Method_CantCompile || bailoutExpected) {
|
||||
JitSpew(JitSpew_BaselineOSR, " Reset WarmUpCounter cantCompile=%s bailoutExpected=%s!",
|
||||
stat == Method_CantCompile ? "yes" : "no",
|
||||
bailoutExpected ? "yes" : "no");
|
||||
script->resetWarmUpCounter();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
MethodStatus
|
||||
jit::Recompile(JSContext* cx, HandleScript script, BaselineFrame* osrFrame, jsbytecode* osrPc,
|
||||
bool constructing, bool force)
|
||||
|
|
|
@ -83,10 +83,9 @@ void SetJitContext(JitContext* ctx);
|
|||
|
||||
bool CanIonCompileScript(JSContext* cx, JSScript* script, bool osr);
|
||||
|
||||
MethodStatus CanEnterAtBranch(JSContext* cx, HandleScript script,
|
||||
BaselineFrame* frame, jsbytecode* pc);
|
||||
bool IonCompileScriptForBaseline(JSContext* cx, BaselineFrame* frame, jsbytecode* pc);
|
||||
|
||||
MethodStatus CanEnter(JSContext* cx, RunState& state);
|
||||
MethodStatus CompileFunctionForBaseline(JSContext* cx, HandleScript script, BaselineFrame* frame);
|
||||
MethodStatus CanEnterUsingFastInvoke(JSContext* cx, HandleScript script, uint32_t numActualArgs);
|
||||
|
||||
MethodStatus
|
||||
|
|
|
@ -1398,6 +1398,11 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
push(scratch);
|
||||
}
|
||||
|
||||
void PushBaselineFramePtr(Register framePtr, Register scratch) {
|
||||
loadBaselineFramePtr(framePtr, scratch);
|
||||
Push(scratch);
|
||||
}
|
||||
|
||||
private:
|
||||
void handleFailure();
|
||||
|
||||
|
|
|
@ -240,6 +240,10 @@ class ICEntry
|
|||
// the prologue).
|
||||
Kind_NonOpCallVM,
|
||||
|
||||
// A fake IC entry for returning from a callVM to after the
|
||||
// warmup counter.
|
||||
Kind_WarmupCounter,
|
||||
|
||||
// A fake IC entry for returning from a callVM to the interrupt
|
||||
// handler via the over-recursion check on function entry.
|
||||
Kind_StackCheck,
|
||||
|
|
Загрузка…
Ссылка в новой задаче