| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeBasePch.h"
- #include "FunctionExecutionStateMachine.h"
- #include "Warnings.h"
- namespace Js
- {
- FunctionExecutionStateMachine::FunctionExecutionStateMachine() :
- owner(nullptr),
- executionState(ExecutionState::Interpreter),
- interpreterLimit(0),
- autoProfilingInterpreter0Limit(0),
- profilingInterpreter0Limit(0),
- autoProfilingInterpreter1Limit(0),
- simpleJitLimit(0),
- profilingInterpreter1Limit(0),
- interpretedCount(0),
- fullJitThreshold(0),
- fullJitRequeueThreshold(0),
- committedProfiledIterations(0),
- lastInterpretedCount(0)
- #if DBG
- ,initializedExecutionModeAndLimits(false)
- ,hasBeenReinitialized(false)
- #ifdef ENABLE_SCRIPT_DEBUGGING
- ,initDebuggerMode(DebuggerMode::NotDebugging)
- ,reinitDebuggerMode(DebuggerMode::NotDebugging)
- #endif
- #endif
- {
- }
- void FunctionExecutionStateMachine::InitializeExecutionModeAndLimits(FunctionBody* functionBody)
- {
- #if DBG
- #ifdef ENABLE_SCRIPT_DEBUGGING
- if (!initializedExecutionModeAndLimits)
- {
- initDebuggerMode = functionBody->GetDebuggerMode();
- }
- #endif
- initializedExecutionModeAndLimits = true;
- #endif
- // Assert we're either uninitialized, or being reinitialized on the same FunctionBody
- Assert(owner == nullptr || owner == functionBody);
- owner = functionBody;
- const ConfigFlagsTable &configFlags = Configuration::Global.flags;
- // AutoProfilingInterpreter might decide to not profile on the first run. For generator
- // functions, that means we will miss the profiling information on the first run when we resume
- // back to the function. This might result in lots of BailOnNoProfile even though we should have
- // profiling information. So always collect profile data for generators
- const bool skipAutoProfileForGenerator = functionBody->SkipAutoProfileForCoroutine();
- interpreterLimit = 0;
- autoProfilingInterpreter0Limit = skipAutoProfileForGenerator ? 0 : static_cast<uint16>(configFlags.AutoProfilingInterpreter0Limit);
- profilingInterpreter0Limit = static_cast<uint16>(configFlags.ProfilingInterpreter0Limit);
- autoProfilingInterpreter1Limit = skipAutoProfileForGenerator ? 0 : static_cast<uint16>(configFlags.AutoProfilingInterpreter1Limit);
- simpleJitLimit = static_cast<uint16>(configFlags.SimpleJitLimit);
- profilingInterpreter1Limit = static_cast<uint16>(configFlags.ProfilingInterpreter1Limit);
- // Based on which execution modes are disabled, calculate the number of additional iterations that need to be covered by
- // the execution mode that will scale with the full JIT threshold
- uint16 scale = 0;
- const bool doInterpreterProfile = owner->DoInterpreterProfile();
- if (!doInterpreterProfile)
- {
- scale +=
- autoProfilingInterpreter0Limit +
- profilingInterpreter0Limit +
- autoProfilingInterpreter1Limit +
- profilingInterpreter1Limit;
- autoProfilingInterpreter0Limit = 0;
- profilingInterpreter0Limit = 0;
- autoProfilingInterpreter1Limit = 0;
- profilingInterpreter1Limit = 0;
- }
- else if (!owner->DoInterpreterAutoProfile())
- {
- scale += autoProfilingInterpreter0Limit + autoProfilingInterpreter1Limit;
- autoProfilingInterpreter0Limit = 0;
- autoProfilingInterpreter1Limit = 0;
- if (!CONFIG_FLAG(NewSimpleJit))
- {
- simpleJitLimit += profilingInterpreter0Limit;
- profilingInterpreter0Limit = 0;
- }
- }
- if (!owner->DoSimpleJit())
- {
- if (!CONFIG_FLAG(NewSimpleJit) && doInterpreterProfile)
- {
- // The old simple JIT is off, but since it does profiling, it will be replaced with the profiling interpreter
- profilingInterpreter1Limit += simpleJitLimit;
- }
- else
- {
- scale += simpleJitLimit;
- }
- simpleJitLimit = 0;
- }
- if (PHASE_OFF(FullJitPhase, owner))
- {
- scale += profilingInterpreter1Limit;
- profilingInterpreter1Limit = 0;
- }
- uint16 fullJitThresholdConfig =
- static_cast<uint16>(
- (skipAutoProfileForGenerator ? 0 : configFlags.AutoProfilingInterpreter0Limit) +
- configFlags.ProfilingInterpreter0Limit +
- (skipAutoProfileForGenerator ? 0 : configFlags.AutoProfilingInterpreter1Limit) +
- configFlags.SimpleJitLimit +
- configFlags.ProfilingInterpreter1Limit);
- if (!configFlags.EnforceExecutionModeLimits)
- {
- /*
- Scale the full JIT threshold based on some heuristics:
- - If the % of code in loops is > 50, scale by 1
- - Byte-code size of code outside loops
- - If the size is < 50, scale by 1.2
- - If the size is < 100, scale by 1.4
- - If the size is >= 100, scale by 1.6
- */
- const uint loopPercentage = owner->GetByteCodeInLoopCount() * 100 / max(1u, owner->GetByteCodeCount());
- const int byteCodeSizeThresholdForInlineCandidate = CONFIG_FLAG(LoopInlineThreshold);
- bool delayFullJITThisFunc =
- (CONFIG_FLAG(DelayFullJITSmallFunc) > 0) && (owner->GetByteCodeWithoutLDACount() <= (uint)byteCodeSizeThresholdForInlineCandidate);
- if (loopPercentage <= 50 || delayFullJITThisFunc)
- {
- const uint straightLineSize = owner->GetByteCodeCount() - owner->GetByteCodeInLoopCount();
- double fullJitDelayMultiplier;
- if (delayFullJITThisFunc)
- {
- fullJitDelayMultiplier = CONFIG_FLAG(DelayFullJITSmallFunc) / 10.0;
- }
- else if (straightLineSize < 50)
- {
- fullJitDelayMultiplier = 1.2;
- }
- else if (straightLineSize < 100)
- {
- fullJitDelayMultiplier = 1.4;
- }
- else
- {
- fullJitDelayMultiplier = 1.6;
- }
- const uint16 newFullJitThreshold = static_cast<uint16>(fullJitThresholdConfig * fullJitDelayMultiplier);
- scale += newFullJitThreshold - fullJitThresholdConfig;
- fullJitThresholdConfig = newFullJitThreshold;
- }
- }
- Assert(fullJitThresholdConfig >= scale);
- fullJitThreshold = fullJitThresholdConfig - scale;
- SetInterpretedCount(0);
- SetDefaultInterpreterExecutionMode();
- SetFullJitThreshold(fullJitThresholdConfig);
- TryTransitionToNextInterpreterExecutionMode();
- }
- void FunctionExecutionStateMachine::ReinitializeExecutionModeAndLimits(FunctionBody* functionBody)
- {
- #if DBG
- hasBeenReinitialized = true;
- #ifdef ENABLE_SCRIPT_DEBUGGING
- reinitDebuggerMode = functionBody->GetDebuggerMode();
- #endif
- #endif
- // TODO: Investigate what it would take to make this invariant hold. Currently fails in AsmJS tests
- // Assert(initializedExecutionModeAndLimits);
- fullJitRequeueThreshold = 0;
- committedProfiledIterations = 0;
- InitializeExecutionModeAndLimits(functionBody);
- }
- bool FunctionExecutionStateMachine::InterpretedSinceCallCountCollection() const
- {
- return interpretedCount != lastInterpretedCount;
- }
- void FunctionExecutionStateMachine::CollectInterpretedCounts()
- {
- lastInterpretedCount = interpretedCount;
- }
- ExecutionMode FunctionExecutionStateMachine::GetExecutionMode() const
- {
- ExecutionMode executionMode = StateToMode(executionState);
- VerifyExecutionMode(executionMode);
- return executionMode;
- }
- void FunctionExecutionStateMachine::SetExecutionState(ExecutionState state)
- {
- // TODO: Investigate what it would take to make this invariant hold
- // Assert(state == GetDefaultInterpreterExecutionState() || IsTerminalState(state));
- VerifyExecutionMode(StateToMode(state));
- executionState = state;
- }
- void FunctionExecutionStateMachine::SetAsmJsExecutionMode()
- {
- SetExecutionState(ExecutionState::FullJit);
- }
- void FunctionExecutionStateMachine::SetDefaultInterpreterExecutionMode()
- {
- SetExecutionState(GetDefaultInterpreterExecutionState());
- }
- FunctionExecutionStateMachine::ExecutionState FunctionExecutionStateMachine::GetDefaultInterpreterExecutionState() const
- {
- if (!owner->DoInterpreterProfile())
- {
- VerifyExecutionMode(ExecutionMode::Interpreter);
- return ExecutionState::Interpreter;
- }
- else if (owner->DoInterpreterAutoProfile())
- {
- VerifyExecutionMode(ExecutionMode::AutoProfilingInterpreter);
- return ExecutionState::AutoProfilingInterpreter0;
- }
- else
- {
- VerifyExecutionMode(ExecutionMode::ProfilingInterpreter);
- return ExecutionState::ProfilingInterpreter0;
- }
- }
- ExecutionMode FunctionExecutionStateMachine::GetInterpreterExecutionMode(const bool isPostBailout)
- {
- Assert(initializedExecutionModeAndLimits);
- if (isPostBailout && owner->DoInterpreterProfile())
- {
- return ExecutionMode::ProfilingInterpreter;
- }
- switch (GetExecutionMode())
- {
- case ExecutionMode::Interpreter:
- case ExecutionMode::AutoProfilingInterpreter:
- case ExecutionMode::ProfilingInterpreter:
- return GetExecutionMode();
- case ExecutionMode::SimpleJit:
- if (CONFIG_FLAG(NewSimpleJit))
- {
- return StateToMode(GetDefaultInterpreterExecutionState());
- }
- // fall through
- case ExecutionMode::FullJit:
- {
- const ExecutionMode executionMode =
- owner->DoInterpreterProfile() ? ExecutionMode::ProfilingInterpreter : ExecutionMode::Interpreter;
- VerifyExecutionMode(executionMode);
- return executionMode;
- }
- default:
- Assert(false);
- __assume(false);
- }
- }
- bool FunctionExecutionStateMachine::IsInterpreterExecutionMode() const
- {
- return GetExecutionMode() <= ExecutionMode::ProfilingInterpreter;
- }
- uint16 FunctionExecutionStateMachine::GetSimpleJitExecutedIterations() const
- {
- Assert(initializedExecutionModeAndLimits);
- Assert(GetExecutionMode() == ExecutionMode::SimpleJit);
- FunctionEntryPointInfo *const simpleJitEntryPointInfo = owner->GetSimpleJitEntryPointInfo();
- if (!simpleJitEntryPointInfo)
- {
- return 0;
- }
- // Simple JIT counts down and transitions on overflow
- const uint32 callCount = simpleJitEntryPointInfo->callsCount;
- Assert(simpleJitLimit == 0 ? callCount == 0 : simpleJitLimit > callCount);
- return callCount == 0 ?
- static_cast<uint16>(simpleJitLimit) :
- static_cast<uint16>(simpleJitLimit) - static_cast<uint16>(callCount) - 1;
- }
- void FunctionExecutionStateMachine::SetSimpleJitCallCount(const uint16 simpleJitLimit) const
- {
- Assert(GetExecutionMode() == ExecutionMode::SimpleJit);
- Assert(owner->GetDefaultFunctionEntryPointInfo() == owner->GetSimpleJitEntryPointInfo());
- // Simple JIT counts down and transitions on overflow
- const uint8 limit = static_cast<uint8>(min(0xffui16, simpleJitLimit));
- owner->GetSimpleJitEntryPointInfo()->callsCount = limit == 0 ? 0 : limit - 1;
- }
- void FunctionExecutionStateMachine::SetFullJitRequeueThreshold(const uint16 newFullJitRequeueThreshold)
- {
- fullJitRequeueThreshold = newFullJitRequeueThreshold;
- }
- void FunctionExecutionStateMachine::SetFullJitThreshold(const uint16 newFullJitThreshold, const bool skipSimpleJit)
- {
- Assert(initializedExecutionModeAndLimits);
- Assert(GetExecutionMode() != ExecutionMode::FullJit);
- int scale = newFullJitThreshold - fullJitThreshold;
- if (scale == 0)
- {
- VerifyExecutionModeLimits();
- return;
- }
- fullJitThreshold = newFullJitThreshold;
- const auto ScaleLimit = [&](uint16 &limit) -> bool
- {
- Assert(scale != 0);
- const int limitScale = max(-static_cast<int>(limit), scale);
- const int newLimit = limit + limitScale;
- Assert(static_cast<int>(static_cast<uint16>(newLimit)) == newLimit);
- limit = static_cast<uint16>(newLimit);
- scale -= limitScale;
- Assert(limit == 0 || scale == 0);
- if (&limit == &simpleJitLimit)
- {
- FunctionEntryPointInfo *const simpleJitEntryPointInfo = owner->GetSimpleJitEntryPointInfo();
- if (owner->GetDefaultFunctionEntryPointInfo() == simpleJitEntryPointInfo)
- {
- Assert(GetExecutionMode() == ExecutionMode::SimpleJit);
- const int newSimpleJitCallCount = max(0, (int)simpleJitEntryPointInfo->callsCount + limitScale);
- Assert(static_cast<int>(static_cast<uint16>(newSimpleJitCallCount)) == newSimpleJitCallCount);
- SetSimpleJitCallCount(static_cast<uint16>(newSimpleJitCallCount));
- }
- }
- return scale == 0;
- };
- /*
- Determine which execution mode's limit scales with the full JIT threshold, in order of preference:
- - New simple JIT
- - Auto-profiling interpreter 1
- - Auto-profiling interpreter 0
- - Interpreter
- - Profiling interpreter 0 (when using old simple JIT)
- - Old simple JIT
- - Profiling interpreter 1
- - Profiling interpreter 0 (when using new simple JIT)
- */
- const bool doSimpleJit = owner->DoSimpleJit();
- const bool doInterpreterProfile = owner->DoInterpreterProfile();
- const bool fullyScaled =
- (CONFIG_FLAG(NewSimpleJit) && doSimpleJit && ScaleLimit(simpleJitLimit)) ||
- (
- doInterpreterProfile
- ? owner->DoInterpreterAutoProfile() &&
- (ScaleLimit(autoProfilingInterpreter1Limit) || ScaleLimit(autoProfilingInterpreter0Limit))
- : ScaleLimit(interpreterLimit)
- ) ||
- (
- CONFIG_FLAG(NewSimpleJit)
- ? doInterpreterProfile &&
- (ScaleLimit(profilingInterpreter1Limit) || ScaleLimit(profilingInterpreter0Limit))
- : (doInterpreterProfile && ScaleLimit(profilingInterpreter0Limit)) ||
- (doSimpleJit && ScaleLimit(simpleJitLimit)) ||
- (doInterpreterProfile && ScaleLimit(profilingInterpreter1Limit))
- );
- Assert(fullyScaled);
- Assert(scale == 0);
- if (GetExecutionMode() != ExecutionMode::SimpleJit)
- {
- Assert(IsInterpreterExecutionMode());
- if (simpleJitLimit != 0 &&
- (skipSimpleJit || simpleJitLimit < DEFAULT_CONFIG_MinSimpleJitIterations) &&
- !PHASE_FORCE(Phase::SimpleJitPhase, owner))
- {
- // Simple JIT code has not yet been generated, and was either requested to be skipped, or the limit was scaled
- // down too much. Skip simple JIT by moving any remaining iterations to an equivalent interpreter execution
- // mode.
- (CONFIG_FLAG(NewSimpleJit) ? autoProfilingInterpreter1Limit : profilingInterpreter1Limit) += simpleJitLimit;
- simpleJitLimit = 0;
- TryTransitionToNextInterpreterExecutionMode();
- }
- }
- VerifyExecutionModeLimits();
- }
- FunctionExecutionStateMachine::ExecutionState FunctionExecutionStateMachine::ModeToState(ExecutionMode mode) const
- {
- switch (mode)
- {
- case ExecutionMode::AutoProfilingInterpreter:
- return ExecutionState::AutoProfilingInterpreter0;
- case ExecutionMode::ProfilingInterpreter:
- return ExecutionState::ProfilingInterpreter0;
- case ExecutionMode::SimpleJit:
- return ExecutionState::SimpleJit;
- case ExecutionMode::FullJit:
- return ExecutionState::FullJit;
- default:
- Assert(!"Unexpected ExecutionMode for ExecutionState");
- // fall through
- case ExecutionMode::Interpreter:
- return ExecutionState::Interpreter;
- }
- }
- ExecutionMode FunctionExecutionStateMachine::StateToMode(ExecutionState state) const
- {
- switch (state)
- {
- case ExecutionState::AutoProfilingInterpreter0:
- case ExecutionState::AutoProfilingInterpreter1:
- return ExecutionMode::AutoProfilingInterpreter;
- case ExecutionState::ProfilingInterpreter0:
- case ExecutionState::ProfilingInterpreter1:
- return ExecutionMode::ProfilingInterpreter;
- case ExecutionState::SimpleJit:
- return ExecutionMode::SimpleJit;
- case ExecutionState::FullJit:
- return ExecutionMode::FullJit;
- default:
- Assert(!"Unexpected ExecutionState for ExecutionMode");
- // fall through
- case ExecutionState::Interpreter:
- return ExecutionMode::Interpreter;
- }
- }
- uint16& FunctionExecutionStateMachine::GetStateLimit(ExecutionState state)
- {
- switch (state)
- {
- case ExecutionState::Interpreter:
- return interpreterLimit;
- case ExecutionState::AutoProfilingInterpreter0:
- return autoProfilingInterpreter0Limit;
- case ExecutionState::AutoProfilingInterpreter1:
- return autoProfilingInterpreter1Limit;
- case ExecutionState::ProfilingInterpreter0:
- return profilingInterpreter0Limit;
- case ExecutionState::ProfilingInterpreter1:
- return profilingInterpreter1Limit;
- case ExecutionState::SimpleJit:
- return simpleJitLimit;
- default:
- Assert(!"Unexpected ExecutionState for limit");
- return interpreterLimit;
- }
- }
- // An execution state is terminal if the current FunctionExecutionStateMachine's limits
- // allow the state to continue to run.
- // FullJit is always a terminal state and is the last terminal state.
- bool FunctionExecutionStateMachine::IsTerminalState(ExecutionState state)
- {
- return state == ExecutionState::FullJit || GetStateLimit(state) != 0;
- }
- // Safely moves from one execution mode to another and updates appropriate class members for the next
- // mode. Note that there are other functions that modify execution state that do not involve this function.
- // This function transitions ExecutionMode as ExecutionState in the following order:
- //
- // +-- Interpreter
- // |
- // | AutoProfilingInterpreter --+
- // | | ^ |
- // | | | v
- // | | | SimpleJit
- // | v | |
- // | ProfilingInterpreter <----+
- // | |
- // | |
- // | v
- // +-> FullJit
- //
- // Transition to the next mode occurs when the limit for the current execution mode reaches 0.
- // Returns true when a transition occurs (i.e., the execution state was updated since the beginning of
- // this function call). Otherwise, returns false to indicate no change in state.
- // See more details of each mode in ExecutionModes.h
- bool FunctionExecutionStateMachine::TryTransitionToNextExecutionMode()
- {
- Assert(initializedExecutionModeAndLimits);
- bool isStateChanged = false;
- if (executionState != ExecutionState::FullJit)
- {
- bool isTransitionNeeded;
- uint16& stateLimit = GetStateLimit(executionState);
- FunctionEntryPointInfo *const simpleJitEntryPointInfo = owner->GetSimpleJitEntryPointInfo();
- // Determine if the current state should not transition when
- // - for non-JITed states, the interpreted count is less than the limit
- // - for JITed states (specifically, SimpleJIT because it can transition), the callsCount
- // is non-zero. CallsCount starts at the limit and decrements to 0 to indicate transition.
- if ((executionState != ExecutionState::SimpleJit && GetInterpretedCount() < stateLimit)
- || (simpleJitEntryPointInfo != nullptr && simpleJitEntryPointInfo->callsCount > 0))
- {
- // Since the current state is under its limit, no transition is needed.
- // Simply verify the current state's execution mode before returning.
- isTransitionNeeded = false;
- }
- else
- {
- // Since the current state's limit is reached, transition from this state to the next state
- // First, save data from the current state
- CommitExecutedIterations(stateLimit, stateLimit);
- // Then, reset data for the next state
- SetInterpretedCount(0);
- isTransitionNeeded = true;
- }
- if (isTransitionNeeded)
- {
- // Keep advancing the state until a terminal state is found or until there are no more
- // states to reach. The path of advancement is described in the banner comment above.
- ExecutionState newState = executionState;
- while (isTransitionNeeded && !IsTerminalState(newState))
- {
- if (newState != ExecutionState::Interpreter)
- {
- // Most states simply advance to the next state
- newState = static_cast<ExecutionState>(static_cast<uint8>(newState) + 1);
- }
- else
- {
- // Interpreter advances straight to FullJit
- newState = ExecutionState::FullJit;
- }
- // If FullJit is the next state, but FullJit is disabled, then no transition
- // is needed.
- if (newState == ExecutionState::FullJit && PHASE_OFF(FullJitPhase, owner))
- {
- isTransitionNeeded = false;
- }
- else
- {
- // Otherwise, transition is needed because there is new state available
- isTransitionNeeded = true;
- }
- }
- // Only update the execution state when the new state is a terminal state
- if (isTransitionNeeded && IsTerminalState(newState))
- {
- Assert(newState != executionState);
- SetExecutionState(newState);
- isStateChanged = true;
- }
- }
- }
- return isStateChanged;
- }
-
- void FunctionExecutionStateMachine::TryTransitionToNextInterpreterExecutionMode()
- {
- Assert(IsInterpreterExecutionMode());
- TryTransitionToNextExecutionMode();
- SetExecutionState(ModeToState(GetInterpreterExecutionMode(false)));
- }
- bool FunctionExecutionStateMachine::TryTransitionToJitExecutionMode()
- {
- const ExecutionMode previousExecutionMode = GetExecutionMode();
- TryTransitionToNextExecutionMode();
- switch (GetExecutionMode())
- {
- case ExecutionMode::SimpleJit:
- break;
- case ExecutionMode::FullJit:
- if (fullJitRequeueThreshold == 0)
- {
- break;
- }
- --fullJitRequeueThreshold;
- return false;
- default:
- return false;
- }
- if (GetExecutionMode() != previousExecutionMode)
- {
- owner->TraceExecutionMode();
- }
- return true;
- }
- void FunctionExecutionStateMachine::TransitionToSimpleJitExecutionMode()
- {
- CommitExecutedIterations();
- interpreterLimit = 0;
- autoProfilingInterpreter0Limit = 0;
- profilingInterpreter0Limit = 0;
- autoProfilingInterpreter1Limit = 0;
- fullJitThreshold = simpleJitLimit + profilingInterpreter1Limit;
- VerifyExecutionModeLimits();
- SetExecutionState(ExecutionState::SimpleJit);
- }
- void FunctionExecutionStateMachine::TransitionToFullJitExecutionMode()
- {
- CommitExecutedIterations();
- interpreterLimit = 0;
- autoProfilingInterpreter0Limit = 0;
- profilingInterpreter0Limit = 0;
- autoProfilingInterpreter1Limit = 0;
- simpleJitLimit = 0;
- profilingInterpreter1Limit = 0;
- fullJitThreshold = 0;
- VerifyExecutionModeLimits();
- SetExecutionState(ExecutionState::FullJit);
- }
- void FunctionExecutionStateMachine::SetIsSpeculativeJitCandidate()
- {
- // This function is a candidate for speculative JIT. Ensure that it is profiled immediately by transitioning out of the
- // auto-profiling interpreter mode.
- if (GetExecutionMode() != ExecutionMode::AutoProfilingInterpreter || GetProfiledIterations() != 0)
- {
- return;
- }
- owner->TraceExecutionMode("IsSpeculativeJitCandidate (before)");
- if (autoProfilingInterpreter0Limit != 0)
- {
- (profilingInterpreter0Limit == 0 ? profilingInterpreter0Limit : autoProfilingInterpreter1Limit) +=
- autoProfilingInterpreter0Limit;
- autoProfilingInterpreter0Limit = 0;
- }
- else if (profilingInterpreter0Limit == 0)
- {
- profilingInterpreter0Limit += autoProfilingInterpreter1Limit;
- autoProfilingInterpreter1Limit = 0;
- }
- owner->TraceExecutionMode("IsSpeculativeJitCandidate");
- TryTransitionToNextInterpreterExecutionMode();
- }
- void FunctionExecutionStateMachine::ResetSimpleJitLimit()
- {
- Assert(initializedExecutionModeAndLimits);
- SetExecutionState(ExecutionState::SimpleJit);
- const uint16 simpleJitNewLimit = static_cast<uint8>(Configuration::Global.flags.SimpleJitLimit);
- if (simpleJitLimit < simpleJitNewLimit)
- {
- fullJitThreshold += simpleJitNewLimit - simpleJitLimit;
- simpleJitLimit = simpleJitNewLimit;
- }
- SetInterpretedCount(0);
- }
- uint16 FunctionExecutionStateMachine::GetProfiledIterations() const
- {
- Assert(initializedExecutionModeAndLimits);
- uint16 profiledIterations = committedProfiledIterations;
- switch (GetExecutionMode())
- {
- case ExecutionMode::ProfilingInterpreter:
- {
- uint32 interpretedCount = GetInterpretedCount();
- const uint16 clampedInterpretedCount =
- interpretedCount <= UINT16_MAX
- ? static_cast<uint16>(interpretedCount)
- : UINT16_MAX;
- const uint16 newProfiledIterations = profiledIterations + clampedInterpretedCount;
- profiledIterations = newProfiledIterations >= profiledIterations ? newProfiledIterations : UINT16_MAX;
- break;
- }
- case ExecutionMode::SimpleJit:
- if (!CONFIG_FLAG(NewSimpleJit))
- {
- const uint16 newProfiledIterations = profiledIterations + GetSimpleJitExecutedIterations();
- profiledIterations = newProfiledIterations >= profiledIterations ? newProfiledIterations : UINT16_MAX;
- }
- break;
- }
- return profiledIterations;
- }
- void FunctionExecutionStateMachine::CommitExecutedIterations()
- {
- Assert(initializedExecutionModeAndLimits);
- switch (GetExecutionMode())
- {
- case ExecutionMode::Interpreter:
- CommitExecutedIterations(interpreterLimit, GetInterpretedCount());
- break;
- case ExecutionMode::AutoProfilingInterpreter:
- CommitExecutedIterations(
- autoProfilingInterpreter0Limit == 0 && profilingInterpreter0Limit == 0
- ? autoProfilingInterpreter1Limit
- : autoProfilingInterpreter0Limit,
- GetInterpretedCount());
- break;
- case ExecutionMode::ProfilingInterpreter:
- CommitExecutedIterations(
- owner->GetSimpleJitEntryPointInfo()
- ? profilingInterpreter1Limit
- : profilingInterpreter0Limit,
- GetInterpretedCount());
- break;
- case ExecutionMode::SimpleJit:
- CommitExecutedIterations(simpleJitLimit, GetSimpleJitExecutedIterations());
- break;
- case ExecutionMode::FullJit:
- break;
- default:
- Assert(false);
- __assume(false);
- }
- }
- void FunctionExecutionStateMachine::CommitExecutedIterations(uint16 &limit, const uint executedIterations)
- {
- Assert(initializedExecutionModeAndLimits);
- Assert(
- &limit == &interpreterLimit ||
- &limit == &autoProfilingInterpreter0Limit ||
- &limit == &profilingInterpreter0Limit ||
- &limit == &autoProfilingInterpreter1Limit ||
- &limit == &simpleJitLimit ||
- &limit == &profilingInterpreter1Limit);
- const uint16 clampedExecutedIterations = executedIterations >= limit ? limit : static_cast<uint16>(executedIterations);
- Assert(fullJitThreshold >= clampedExecutedIterations);
- fullJitThreshold -= clampedExecutedIterations;
- limit -= clampedExecutedIterations;
- VerifyExecutionModeLimits();
- if (&limit == &profilingInterpreter0Limit ||
- (!CONFIG_FLAG(NewSimpleJit) && &limit == &simpleJitLimit) ||
- &limit == &profilingInterpreter1Limit)
- {
- const uint16 newCommittedProfiledIterations = committedProfiledIterations + clampedExecutedIterations;
- committedProfiledIterations =
- newCommittedProfiledIterations >= committedProfiledIterations ? newCommittedProfiledIterations : UINT16_MAX;
- }
- }
- void FunctionExecutionStateMachine::VerifyExecutionModeLimits() const
- {
- Assert(initializedExecutionModeAndLimits);
- Assert(
- (
- interpreterLimit +
- autoProfilingInterpreter0Limit +
- profilingInterpreter0Limit +
- autoProfilingInterpreter1Limit +
- simpleJitLimit +
- profilingInterpreter1Limit
- ) == fullJitThreshold);
- }
- void FunctionExecutionStateMachine::VerifyExecutionMode(const ExecutionMode executionMode) const
- {
- #if DBG
- Assert(initializedExecutionModeAndLimits);
- Assert(executionMode < ExecutionMode::Count);
- switch (executionMode)
- {
- case ExecutionMode::Interpreter:
- Assert(!owner->DoInterpreterProfile());
- break;
- case ExecutionMode::AutoProfilingInterpreter:
- Assert(owner->DoInterpreterProfile());
- Assert(owner->DoInterpreterAutoProfile());
- break;
- case ExecutionMode::ProfilingInterpreter:
- Assert(owner->DoInterpreterProfile());
- break;
- case ExecutionMode::SimpleJit:
- Assert(owner->DoSimpleJit());
- break;
- case ExecutionMode::FullJit:
- Assert(!PHASE_OFF(FullJitPhase, owner));
- break;
- default:
- Assert(false);
- __assume(false);
- }
- #else
- UNREFERENCED_PARAMETER(executionMode);
- #endif
- }
- void FunctionExecutionStateMachine::PrintLimits() const
- {
- Output::Print(
- _u("limits: %hu.%hu.%hu.%hu.%hu = %hu"),
- interpreterLimit + autoProfilingInterpreter0Limit,
- profilingInterpreter0Limit,
- autoProfilingInterpreter1Limit,
- simpleJitLimit,
- profilingInterpreter1Limit,
- fullJitThreshold);
- }
- void FunctionExecutionStateMachine::AssertIsInitialized() const
- {
- #if DBG
- Assert(initializedExecutionModeAndLimits);
- Assert(owner != nullptr);
- #endif
- }
- }
|