blob: ba52edee3e4cc40d4b4be8ab217f10fa18d1319e [file]
//-------------------------------------------------------------------------------------------------------
// 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
{
}
uint16 FunctionExecutionStateMachine::GetDefaultAutoProfilingInterpreter0Limit(bool isCoroutine) const {
return isCoroutine ? 0 : static_cast<uint16>(Configuration::Global.flags.AutoProfilingInterpreter0Limit);
}
uint16 FunctionExecutionStateMachine::GetDefaultProfilingInterpreter0Limit(bool isCoroutine) const {
if (isCoroutine)
{
return static_cast<uint16>(Configuration::Global.flags.AutoProfilingInterpreter0Limit) +
static_cast<uint16>(Configuration::Global.flags.AutoProfilingInterpreter1Limit);
}
return static_cast<uint16>(Configuration::Global.flags.ProfilingInterpreter0Limit);
}
uint16 FunctionExecutionStateMachine::GetDefaultAutoProfilingInterpreter1Limit(bool isCoroutine) const {
return isCoroutine ? 0 : static_cast<uint16>(Configuration::Global.flags.AutoProfilingInterpreter1Limit);
}
uint16 FunctionExecutionStateMachine::GetDefaultSimpleJitLimit(bool isCoroutine) const {
return static_cast<uint16>(Configuration::Global.flags.SimpleJitLimit);
}
uint16 FunctionExecutionStateMachine::GetDefaultProfilingInterpreter1Limit(bool isCoroutine) const {
return static_cast<uint16>(Configuration::Global.flags.ProfilingInterpreter1Limit);
}
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;
// 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.
const bool isCoroutine = functionBody->SkipAutoProfileForCoroutine();
interpreterLimit = 0;
autoProfilingInterpreter0Limit = GetDefaultAutoProfilingInterpreter0Limit(isCoroutine);
profilingInterpreter0Limit = GetDefaultProfilingInterpreter0Limit(isCoroutine);
autoProfilingInterpreter1Limit = GetDefaultAutoProfilingInterpreter1Limit(isCoroutine);
simpleJitLimit = GetDefaultSimpleJitLimit(isCoroutine);
profilingInterpreter1Limit = GetDefaultProfilingInterpreter1Limit(isCoroutine);
// 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 = GetDefaultFullJitThreshold(isCoroutine);
if (!Configuration::Global.flags.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();
}
uint16 FunctionExecutionStateMachine::GetDefaultFullJitThreshold(bool isCoroutine) const
{
return GetDefaultAutoProfilingInterpreter0Limit(isCoroutine) +
GetDefaultProfilingInterpreter0Limit(isCoroutine) +
GetDefaultAutoProfilingInterpreter1Limit(isCoroutine) +
GetDefaultSimpleJitLimit(isCoroutine) +
GetDefaultProfilingInterpreter1Limit(isCoroutine);
}
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
}
}