| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- //-------------------------------------------------------------------------------------------------------
- // 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)
- , codeAddress(NULL)
- , functionBody(functionBody)
- , type(type)
- , jitMode(ExecutionMode::Interpreter)
- , entryPointInfo(entryPointInfo)
- , recyclableData(nullptr)
- , isInJitQueue(false)
- , isAllocationCommitted(false)
- , isJitInDebugMode(isJitInDebugMode)
- , queuedFullJitWorkItem(nullptr)
- , allocation(nullptr)
- #ifdef IR_VIEWER
- , isRejitIRViewerFunction(false)
- , irViewerOutput(nullptr)
- , irViewerRequestContext(nullptr)
- #endif
- {
- }
- 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(!functionBody->DoFullJit())
- {
- 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::RecordNativeCodeSize(Func *func, size_t bytes, ushort pdataCount, ushort xdataSize)
- {
- BYTE *buffer;
- #if defined(_M_ARM32_OR_ARM64)
- bool canAllocInPreReservedHeapPageSegment = false;
- #else
- bool canAllocInPreReservedHeapPageSegment = func->CanAllocInPreReservedHeapPageSegment();
- #endif
- EmitBufferAllocation *allocation = func->GetEmitBufferManager()->AllocateBuffer(bytes, &buffer, false, pdataCount, xdataSize, canAllocInPreReservedHeapPageSegment, true);
- Assert(allocation != nullptr);
- if (buffer == nullptr)
- Js::Throw::OutOfMemory();
- SetCodeAddress((size_t)buffer);
- SetCodeSize(bytes);
- SetPdataCount(pdataCount);
- SetXdataSize(xdataSize);
- SetAllocation(allocation);
- }
- void CodeGenWorkItem::RecordNativeCode(Func *func, const BYTE* sourceBuffer)
- {
- if (!func->GetEmitBufferManager()->CommitBuffer(this->GetAllocation(), (BYTE *)GetCodeAddress(), GetCodeSize(), sourceBuffer))
- {
- Js::Throw::OutOfMemory();
- }
- this->isAllocationCommitted = true;
- #if DBG_DUMP
- if (Type() == JsLoopBodyWorkItemType)
- {
- func->GetEmitBufferManager()->totalBytesLoopBody += GetCodeSize();
- }
- #endif
- }
- 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);
- }
- }
- void CodeGenWorkItem::FinalizeNativeCode(Func *func)
- {
- NativeCodeData * data = func->GetNativeCodeDataAllocator()->Finalize();
- NativeCodeData * transferData = func->GetTransferDataAllocator()->Finalize();
- CodeGenNumberChunk * numberChunks = func->GetNumberAllocator()->Finalize();
- this->functionBody->RecordNativeBaseAddress((BYTE *)GetCodeAddress(), GetCodeSize(), data, transferData, numberChunks, GetEntryPoint(), GetLoopNumber());
- func->GetEmitBufferManager()->CompletePreviousAllocation(this->GetAllocation());
- }
- QueuedFullJitWorkItem *CodeGenWorkItem::GetQueuedFullJitWorkItem() const
- {
- return queuedFullJitWorkItem;
- }
- QueuedFullJitWorkItem *CodeGenWorkItem::EnsureQueuedFullJitWorkItem()
- {
- if(queuedFullJitWorkItem)
- {
- return queuedFullJitWorkItem;
- }
- queuedFullJitWorkItem = HeapNewNoThrow(QueuedFullJitWorkItem, this);
- return queuedFullJitWorkItem;
- }
|