blob: 968537f82dc864d53364578140c4ab711794e1b2 [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 "Backend.h"
#include "Language/SourceDynamicProfileManager.h"
CodeGenWorkItem::CodeGenWorkItem(
JsUtil::JobManager *const manager,
Js::FunctionBody *const functionBody,
Js::EntryPointInfo* entryPointInfo,
bool isJitInDebugMode,
CodeGenWorkItemType type)
: JsUtil::Job(manager)
, functionBody(functionBody)
, entryPointInfo(entryPointInfo)
, recyclableData(nullptr)
, isInJitQueue(false)
, isAllocationCommitted(false)
, queuedFullJitWorkItem(nullptr)
, allocation(nullptr)
#ifdef IR_VIEWER
, isRejitIRViewerFunction(false)
, irViewerOutput(nullptr)
, irViewerRequestContext(nullptr)
#endif
{
this->jitData = {0};
// work item data
this->jitData.type = type;
this->jitData.isJitInDebugMode = isJitInDebugMode;
ResetJitMode();
}
CodeGenWorkItem::~CodeGenWorkItem()
{
if(queuedFullJitWorkItem)
{
HeapDelete(queuedFullJitWorkItem);
}
}
//
// Helps determine whether a function should be speculatively jitted.
// This function is only used once and is used in a time-critical area, so
// be careful with it (moving it around actually caused around a 5% perf
// regression on a test).
//
bool CodeGenWorkItem::ShouldSpeculativelyJit(uint byteCodeSizeGenerated) const
{
if(PHASE_OFF(Js::FullJitPhase, this->functionBody))
{
return false;
}
byteCodeSizeGenerated += this->GetByteCodeCount();
if(CONFIG_FLAG(ProfileBasedSpeculativeJit))
{
Assert(!CONFIG_ISENABLED(Js::NoDynamicProfileInMemoryCacheFlag));
// JIT this now if we are under the speculation cap.
return
byteCodeSizeGenerated < (uint)CONFIG_FLAG(SpeculationCap) ||
(
byteCodeSizeGenerated < (uint)CONFIG_FLAG(ProfileBasedSpeculationCap) &&
this->ShouldSpeculativelyJitBasedOnProfile()
);
}
else
{
return byteCodeSizeGenerated < (uint)CONFIG_FLAG(SpeculationCap);
}
}
bool CodeGenWorkItem::ShouldSpeculativelyJitBasedOnProfile() const
{
Js::FunctionBody* functionBody = this->GetFunctionBody();
uint loopPercentage = (functionBody->GetByteCodeInLoopCount()*100) / (functionBody->GetByteCodeCount() + 1);
uint straightLineSize = functionBody->GetByteCodeCount() - functionBody->GetByteCodeInLoopCount();
// This ensures only small and loopy functions are prejitted.
if(loopPercentage >= 50 || straightLineSize < 300)
{
Js::SourceDynamicProfileManager* profileManager = functionBody->GetSourceContextInfo()->sourceDynamicProfileManager;
if(profileManager != nullptr)
{
functionBody->SetIsSpeculativeJitCandidate();
if(!functionBody->HasDynamicProfileInfo())
{
return false;
}
Js::ExecutionFlags executionFlags = profileManager->IsFunctionExecuted(functionBody->GetLocalFunctionId());
if(executionFlags == Js::ExecutionFlags_Executed)
{
return true;
}
}
}
return false;
}
/*
A comment about how to cause certain phases to only be on:
INT = Interpreted, SJ = SimpleJit, FJ = FullJit
To get only the following levels on, use the flags:
INT: -noNative
SJ : -forceNative -off:fullJit
FJ : -forceNative -off:simpleJit
INT, SJ: -off:fullJit
INT, FJ: -off:simpleJit
SJ, FG: -forceNative
INT, SJ, FG: (default)
*/
void CodeGenWorkItem::OnAddToJitQueue()
{
Assert(!this->isInJitQueue);
this->isInJitQueue = true;
VerifyJitMode();
this->entryPointInfo->SetCodeGenQueued();
if(IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_QUEUED()))
{
WCHAR displayNameBuffer[256];
WCHAR* displayName = displayNameBuffer;
size_t sizeInChars = this->GetDisplayName(displayName, 256);
if(sizeInChars > 256)
{
displayName = HeapNewArray(WCHAR, sizeInChars);
this->GetDisplayName(displayName, 256);
}
JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_QUEUED(
this->GetFunctionNumber(),
displayName,
this->GetScriptContext(),
this->GetInterpretedCount()));
if(displayName != displayNameBuffer)
{
HeapDeleteArray(sizeInChars, displayName);
}
}
}
void CodeGenWorkItem::OnRemoveFromJitQueue(NativeCodeGenerator* generator)
{
// This is called from within the lock
this->isInJitQueue = false;
this->entryPointInfo->SetCodeGenPending();
functionBody->GetScriptContext()->GetThreadContext()->UnregisterCodeGenRecyclableData(this->recyclableData);
this->recyclableData = nullptr;
if(IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_DEQUEUED()))
{
WCHAR displayNameBuffer[256];
WCHAR* displayName = displayNameBuffer;
size_t sizeInChars = this->GetDisplayName(displayName, 256);
if(sizeInChars > 256)
{
displayName = HeapNewArray(WCHAR, sizeInChars);
this->GetDisplayName(displayName, 256);
}
JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_DEQUEUED(
this->GetFunctionNumber(),
displayName,
this->GetScriptContext(),
this->GetInterpretedCount()));
if(displayName != displayNameBuffer)
{
HeapDeleteArray(sizeInChars, displayName);
}
}
if(this->Type() == JsLoopBodyWorkItemType)
{
// Go ahead and delete it and let it re-queue if more interpreting of the loop happens
auto loopBodyWorkItem = static_cast<JsLoopBodyCodeGen*>(this);
loopBodyWorkItem->loopHeader->ResetInterpreterCount();
loopBodyWorkItem->GetEntryPoint()->Reset();
HeapDelete(loopBodyWorkItem);
}
else
{
Assert(GetJitMode() == ExecutionMode::FullJit); // simple JIT work items are not removed from the queue
GetFunctionBody()->OnFullJitDequeued(static_cast<Js::FunctionEntryPointInfo *>(GetEntryPoint()));
// Add it back to the list of available functions to be jitted
generator->AddWorkItem(this);
}
}
void CodeGenWorkItem::OnWorkItemProcessFail(NativeCodeGenerator* codeGen)
{
if (!isAllocationCommitted && this->allocation != nullptr && this->allocation->allocation != nullptr)
{
#if DBG
this->allocation->allocation->isNotExecutableBecauseOOM = true;
#endif
codeGen->FreeNativeCodeGenAllocation(this->allocation->allocation->address, nullptr);
}
}
QueuedFullJitWorkItem *CodeGenWorkItem::GetQueuedFullJitWorkItem() const
{
return queuedFullJitWorkItem;
}
QueuedFullJitWorkItem *CodeGenWorkItem::EnsureQueuedFullJitWorkItem()
{
if(queuedFullJitWorkItem)
{
return queuedFullJitWorkItem;
}
queuedFullJitWorkItem = HeapNewNoThrow(QueuedFullJitWorkItem, this);
return queuedFullJitWorkItem;
}