blob: dc87262b272fa654225c9af4d89ebd67a54d6b0f [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
struct CodeGenWorkItem;
struct JsFunctionCodeGen;
struct JsLoopBodyCodeGen;
class InliningDecider;
class ObjTypeSpecFldInfo;
namespace Js
{
class FunctionCodeGenJitTimeData;
class RemoteScriptContext;
};
class NativeCodeGenerator sealed : public JsUtil::WaitableJobManager
{
#if ENABLE_DEBUG_CONFIG_OPTIONS
static volatile UINT_PTR CodegenFailureSeed;
#endif
friend JsUtil::ForegroundJobProcessor;
friend JsUtil::BackgroundJobProcessor;
friend Js::RemoteScriptContext;
public:
NativeCodeGenerator(Js::ScriptContext * scriptContext);
~NativeCodeGenerator();
void Close();
JsFunctionCodeGen * NewFunctionCodeGen(Js::FunctionBody *functionBody, Js::EntryPointInfo* info);
JsLoopBodyCodeGen * NewLoopBodyCodeGen(Js::FunctionBody *functionBody, Js::EntryPointInfo* info, Js::LoopHeader * loopHeader);
bool GenerateFunction(Js::FunctionBody * fn, Js::ScriptFunction * function = nullptr);
void GenerateLoopBody(Js::FunctionBody * functionBody, Js::LoopHeader * loopHeader, Js::EntryPointInfo* info = nullptr, uint localCount = 0, Js::Var localSlots[] = nullptr);
static bool IsValidVar(const Js::Var var, Recycler *const recycler);
#ifdef ENABLE_PREJIT
void GenerateAllFunctions(Js::FunctionBody * fn);
bool DoBackEnd(Js::FunctionBody * fn);
#endif
#ifdef IR_VIEWER
Js::Var RejitIRViewerFunction(Js::FunctionBody *fn, Js::ScriptContext *scriptContext);
#endif
void SetProfileMode(BOOL fSet);
public:
static Js::Var CheckCodeGenThunk(Js::RecyclableObject* function, Js::CallInfo callInfo, ...);
#ifdef ASMJS_PLAT
static Js::Var CheckAsmJsCodeGenThunk(Js::RecyclableObject* function, Js::CallInfo callInfo, ...);
#endif
static bool IsThunk(Js::JavascriptMethod codeAddress);
static bool IsAsmJsCodeGenThunk(Js::JavascriptMethod codeAddress);
static CheckCodeGenFunction GetCheckCodeGenFunction(Js::JavascriptMethod codeAddress);
static Js::JavascriptMethod CheckCodeGen(Js::ScriptFunction * function);
static Js::Var CheckAsmJsCodeGen(Js::ScriptFunction * function);
public:
static void Jit_TransitionFromSimpleJit(void *const framePointer);
private:
static void TransitionFromSimpleJit(Js::ScriptFunction *const function);
private:
static Js::JavascriptMethod CheckCodeGenDone(Js::FunctionBody *const functionBody, Js::FunctionEntryPointInfo *const entryPointInfo, Js::ScriptFunction * function);
CodeGenWorkItem *GetJob(Js::EntryPointInfo *const entryPoint) const;
bool WasAddedToJobProcessor(JsUtil::Job *const job) const;
bool ShouldProcessInForeground(const bool willWaitForJob, const unsigned int numJobsInQueue) const;
void Prioritize(JsUtil::Job *const job, const bool forceAddJobToProcessor = false, void* function = nullptr);
void PrioritizedButNotYetProcessed(JsUtil::Job *const job);
void BeforeWaitForJob(Js::EntryPointInfo *const entryPoint) const;
void AfterWaitForJob(Js::EntryPointInfo *const entryPoint) const;
static bool WorkItemExceedsJITLimits(CodeGenWorkItem *const codeGenWork);
virtual bool Process(JsUtil::Job *const job, JsUtil::ParallelThreadData *threadData) override;
virtual void JobProcessed(JsUtil::Job *const job, const bool succeeded) override;
JsUtil::Job *GetJobToProcessProactively();
void AddToJitQueue(CodeGenWorkItem *const codeGenWorkItem, bool prioritize, bool lock, void* function = nullptr);
void RemoveProactiveJobs();
void UpdateJITState();
static void LogCodeGenStart(CodeGenWorkItem * workItem, LARGE_INTEGER * start_time);
static void LogCodeGenDone(CodeGenWorkItem * workItem, LARGE_INTEGER * start_time);
typedef SListCounted<ObjTypeSpecFldInfo*, ArenaAllocator> ObjTypeSpecFldInfoList;
template<bool IsInlinee> void GatherCodeGenData(
Recycler *const recycler,
Js::FunctionBody *const topFunctionBody,
Js::FunctionBody *const functionBody,
Js::EntryPointInfo *const entryPoint,
InliningDecider &inliningDecider,
ObjTypeSpecFldInfoList *objTypeSpecFldInfoList,
Js::FunctionCodeGenJitTimeData *const jitTimeData,
Js::FunctionCodeGenRuntimeData *const runtimeData,
Js::JavascriptFunction* function = nullptr,
bool isJitTimeDataComputed = false,
uint32 recursiveInlineDepth = 0);
Js::CodeGenRecyclableData *GatherCodeGenData(Js::FunctionBody *const topFunctionBody, Js::FunctionBody *const functionBody, Js::EntryPointInfo *const entryPoint, CodeGenWorkItem* workItem, void* function = nullptr);
public:
void UpdateQueueForDebugMode();
bool IsBackgroundJIT() const;
void EnterScriptStart();
void FreeNativeCodeGenAllocation(void* codeAddress, void* thunkAddress);
bool TryReleaseNonHiPriWorkItem(CodeGenWorkItem* workItem);
void QueueFreeNativeCodeGenAllocation(void* codeAddress, void* thunkAddress);
bool IsClosed() { return isClosed; }
void AddWorkItem(CodeGenWorkItem* workItem);
InProcCodeGenAllocators* GetCodeGenAllocator(PageAllocator* pageallocator){ return EnsureForegroundAllocators(pageallocator); }
#if DBG_DUMP
FILE * asmFile;
#endif
#ifdef PROFILE_EXEC
void CreateProfiler(Js::ScriptContextProfiler * profiler);
void SetProfilerFromNativeCodeGen(NativeCodeGenerator * nativeCodeGen);
Js::ScriptContextProfiler *EnsureForegroundCodeGenProfiler();
static void ProfileBegin(Js::ScriptContextProfiler *const profiler, Js::Phase);
static void ProfileEnd(Js::ScriptContextProfiler *const profiler, Js::Phase);
void ProfilePrint();
#endif
private:
void CodeGen(PageAllocator * pageAllocator, CodeGenWorkItem* workItem, const bool foreground);
InProcCodeGenAllocators *CreateAllocators(PageAllocator *const pageAllocator)
{
return HeapNew(InProcCodeGenAllocators, pageAllocator->GetAllocationPolicyManager(), scriptContext, scriptContext->GetThreadContext()->GetCodePageAllocators(), GetCurrentProcess());
}
InProcCodeGenAllocators *EnsureForegroundAllocators(PageAllocator * pageAllocator)
{
if (this->foregroundAllocators == nullptr)
{
this->foregroundAllocators = CreateAllocators(pageAllocator);
#if !_M_X64_OR_ARM64 && _CONTROL_FLOW_GUARD
if (this->scriptContext->webWorkerId != Js::Constants::NonWebWorkerContextId)
{
this->foregroundAllocators->canCreatePreReservedSegment = true;
}
#endif
}
return this->foregroundAllocators;
}
InProcCodeGenAllocators * GetBackgroundAllocator(PageAllocator *pageAllocator)
{
return this->backgroundAllocators;
}
Js::ScriptContextProfiler * GetBackgroundCodeGenProfiler(PageAllocator *allocator);
void AllocateBackgroundCodeGenProfiler(PageAllocator * pageAllocator);
void AllocateBackgroundAllocators(PageAllocator * pageAllocator)
{
if (!this->backgroundAllocators)
{
this->backgroundAllocators = CreateAllocators(pageAllocator);
#if !_M_X64_OR_ARM64 && _CONTROL_FLOW_GUARD
this->backgroundAllocators->canCreatePreReservedSegment = true;
#endif
}
AllocateBackgroundCodeGenProfiler(pageAllocator);
}
virtual void ProcessorThreadSpecificCallBack(PageAllocator * pageAllocator) override
{
AllocateBackgroundAllocators(pageAllocator);
}
static ExecutionMode PrejitJitMode(Js::FunctionBody *const functionBody);
bool TryAggressiveInlining(Js::FunctionBody *const topFunctionBody, Js::FunctionBody *const functionBody, InliningDecider &inliningDecider, uint32& inlineeCount, uint recursiveInlineDepth);
private:
Js::ScriptContext * scriptContext;
Js::FunctionBody::SetNativeEntryPointFuncType SetNativeEntryPoint;
uint pendingCodeGenWorkItems;
JsUtil::DoublyLinkedList<CodeGenWorkItem> workItems;
JsUtil::DoublyLinkedList<QueuedFullJitWorkItem> queuedFullJitWorkItems;
uint queuedFullJitWorkItemCount;
uint byteCodeSizeGenerated;
bool isOptimizedForManyInstances;
bool isClosed;
bool hasUpdatedQForDebugMode;
class FreeLoopBodyJob: public JsUtil::Job
{
public:
FreeLoopBodyJob(JsUtil::JobManager *const manager, void* codeAddress, void* thunkAddress, bool isHeapAllocated = true):
JsUtil::Job(manager),
codeAddress(codeAddress),
thunkAddress(thunkAddress),
heapAllocated(isHeapAllocated)
{
}
bool heapAllocated;
void* codeAddress;
void* thunkAddress;
};
class FreeLoopBodyJobManager sealed: public WaitableJobManager
{
public:
FreeLoopBodyJobManager(JsUtil::JobProcessor* processor)
: JsUtil::WaitableJobManager(processor)
, autoClose(true)
, isClosed(false)
, stackJobProcessed(false)
#if DBG
, waitingForStackJob(false)
#endif
{
Processor()->AddManager(this);
}
virtual ~FreeLoopBodyJobManager()
{
if (autoClose && !isClosed)
{
Close();
}
Assert(this->isClosed);
}
void Close()
{
Assert(!this->isClosed);
Processor()->RemoveManager(this);
this->isClosed = true;
}
void SetAutoClose(bool autoClose)
{
this->autoClose = autoClose;
}
FreeLoopBodyJob* GetJob(FreeLoopBodyJob* job)
{
if (!job->heapAllocated)
{
return this->stackJobProcessed ? nullptr : job;
}
else
{
return job;
}
}
bool WasAddedToJobProcessor(JsUtil::Job *const job) const
{
return true;
}
void SetNativeCodeGen(NativeCodeGenerator* nativeCodeGen)
{
this->nativeCodeGen = nativeCodeGen;
}
void BeforeWaitForJob(FreeLoopBodyJob*) const {}
void AfterWaitForJob(FreeLoopBodyJob*) const {}
virtual bool Process(JsUtil::Job *const job, JsUtil::ParallelThreadData *threadData) override
{
FreeLoopBodyJob* freeLoopBodyJob = static_cast<FreeLoopBodyJob*>(job);
// Free Loop Body
nativeCodeGen->FreeNativeCodeGenAllocation(freeLoopBodyJob->codeAddress, freeLoopBodyJob->thunkAddress);
return true;
}
virtual void JobProcessed(JsUtil::Job *const job, const bool succeeded) override
{
FreeLoopBodyJob* freeLoopBodyJob = static_cast<FreeLoopBodyJob*>(job);
if (freeLoopBodyJob->heapAllocated)
{
HeapDelete(freeLoopBodyJob);
}
else
{
#if DBG
Assert(this->waitingForStackJob);
this->waitingForStackJob = false;
#endif
this->stackJobProcessed = true;
}
}
void QueueFreeLoopBodyJob(void* codeAddress, void* thunkAddress);
private:
NativeCodeGenerator* nativeCodeGen;
bool autoClose;
bool isClosed;
bool stackJobProcessed;
#if DBG
bool waitingForStackJob;
#endif
};
FreeLoopBodyJobManager freeLoopBodyManager;
InProcCodeGenAllocators * foregroundAllocators;
InProcCodeGenAllocators * backgroundAllocators;
#ifdef PROFILE_EXEC
Js::ScriptContextProfiler * foregroundCodeGenProfiler;
Js::ScriptContextProfiler * backgroundCodeGenProfiler;
#endif
#if DBG
ThreadContextId mainThreadId;
friend void CheckIsExecutable(Js::RecyclableObject * function, Js::JavascriptMethod entrypoint);
#endif
};