blob: 33a0e85e1aea7ccbdeda8a7a7ca86b059627a653 [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// 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"
FunctionJITTimeInfo::FunctionJITTimeInfo(FunctionJITTimeDataIDL * data) : m_data(*data)
{
// we will cast the data (i.e. midl struct) pointers into info pointers so we can extend with methods
CompileAssert(sizeof(FunctionJITTimeDataIDL) == sizeof(FunctionJITTimeInfo));
}
/* static */
void
FunctionJITTimeInfo::BuildJITTimeData(
__in ArenaAllocator * alloc,
__in const Js::FunctionCodeGenJitTimeData * codeGenData,
__in_opt const Js::FunctionCodeGenRuntimeData * runtimeData,
__out FunctionJITTimeDataIDL * jitData,
bool isInlinee,
bool isForegroundJIT)
{
jitData->functionInfoAddr = (intptr_t)codeGenData->GetFunctionInfo();
jitData->localFuncId = codeGenData->GetFunctionInfo()->GetLocalFunctionId();
jitData->isAggressiveInliningEnabled = codeGenData->GetIsAggressiveInliningEnabled();
jitData->isInlined = codeGenData->GetIsInlined();
jitData->weakFuncRef = (intptr_t)codeGenData->GetWeakFuncRef();
jitData->inlineesBv = (BVFixedIDL*)(const BVFixed*)codeGenData->inlineesBv;
jitData->entryPointInfoAddr = (intptr_t)codeGenData->GetEntryPointInfo();
if (!codeGenData->GetFunctionBody() || !codeGenData->GetFunctionBody()->GetByteCode())
{
// outermost function must have a body, but inlinees may not (if they are builtins)
Assert(isInlinee);
return;
}
Js::FunctionBody * body = codeGenData->GetFunctionInfo()->GetParseableFunctionInfo()->GetFunctionBody();
jitData->bodyData = AnewStructZ(alloc, FunctionBodyDataIDL);
JITTimeFunctionBody::InitializeJITFunctionData(alloc, body, jitData->bodyData);
Assert(isInlinee == !!runtimeData);
const Js::FunctionCodeGenRuntimeData * targetRuntimeData = nullptr;
if (runtimeData)
{
// may be polymorphic, so seek the runtime data matching our JIT time data
targetRuntimeData = runtimeData->GetForTarget(codeGenData->GetFunctionInfo()->GetFunctionBody());
}
Js::FunctionBody * functionBody = codeGenData->GetFunctionBody();
if (functionBody->HasDynamicProfileInfo())
{
ProfileDataIDL * profileData = AnewStruct(alloc, ProfileDataIDL);
JITTimeProfileInfo::InitializeJITProfileData(alloc, functionBody->GetAnyDynamicProfileInfo(), functionBody, profileData, isForegroundJIT);
jitData->bodyData->profileData = profileData;
if (isInlinee)
{
// if not inlinee, NativeCodeGenerator will provide the address
// REVIEW: OOP JIT, for inlinees, is this actually necessary?
Js::ProxyEntryPointInfo *defaultEntryPointInfo = functionBody->GetDefaultEntryPointInfo();
Assert(defaultEntryPointInfo->IsFunctionEntryPointInfo());
Js::FunctionEntryPointInfo *functionEntryPointInfo = static_cast<Js::FunctionEntryPointInfo*>(defaultEntryPointInfo);
jitData->callsCountAddress = (intptr_t)&functionEntryPointInfo->callsCount;
jitData->sharedPropertyGuards = codeGenData->sharedPropertyGuards;
jitData->sharedPropGuardCount = codeGenData->sharedPropertyGuardCount;
}
}
if (jitData->bodyData->profiledCallSiteCount > 0)
{
jitData->inlineeCount = jitData->bodyData->profiledCallSiteCount;
// using arena because we can't recycler allocate (may be on background), and heap freeing this is slightly complicated
jitData->inlinees = AnewArrayZ(alloc, FunctionJITTimeDataIDL*, jitData->bodyData->profiledCallSiteCount);
jitData->inlineesRecursionFlags = AnewArrayZ(alloc, boolean, jitData->bodyData->profiledCallSiteCount);
for (Js::ProfileId i = 0; i < jitData->bodyData->profiledCallSiteCount; ++i)
{
const Js::FunctionCodeGenJitTimeData * inlineeJITData = codeGenData->GetInlinee(i);
if (inlineeJITData == codeGenData)
{
jitData->inlineesRecursionFlags[i] = TRUE;
}
else if (inlineeJITData != nullptr)
{
const Js::FunctionCodeGenRuntimeData * inlineeRuntimeData = nullptr;
if (inlineeJITData->GetFunctionInfo()->HasBody())
{
inlineeRuntimeData = isInlinee ? targetRuntimeData->GetInlinee(i) : functionBody->GetInlineeCodeGenRuntimeData(i);
}
jitData->inlinees[i] = AnewStructZ(alloc, FunctionJITTimeDataIDL);
BuildJITTimeData(alloc, inlineeJITData, inlineeRuntimeData, jitData->inlinees[i], true, isForegroundJIT);
}
}
jitData->callbackInlinees = AnewArrayZ(alloc, FunctionJITTimeDataIDL*, jitData->bodyData->profiledCallSiteCount);
for (Js::ProfileId i = 0; i < jitData->bodyData->profiledCallSiteCount; ++i)
{
const Js::FunctionCodeGenJitTimeData * inlineeJITData = codeGenData->GetCallbackInlinee(i);
if (inlineeJITData != nullptr)
{
const Js::FunctionCodeGenRuntimeData * inlineeRuntimeData = nullptr;
if (inlineeJITData->GetFunctionInfo()->HasBody())
{
inlineeRuntimeData = isInlinee ? targetRuntimeData->GetCallbackInlinee(i) : functionBody->GetCallbackInlineeCodeGenRuntimeData(i);
}
jitData->callbackInlinees[i] = AnewStructZ(alloc, FunctionJITTimeDataIDL);
BuildJITTimeData(alloc, inlineeJITData, inlineeRuntimeData, jitData->callbackInlinees[i], true, isForegroundJIT);
}
}
jitData->callApplyTargetInlineeCount = jitData->bodyData->profiledCallApplyCallSiteCount;
if (jitData->bodyData->profiledCallApplyCallSiteCount > 0)
{
jitData->callApplyTargetInlinees = AnewArrayZ(alloc, FunctionJITTimeDataIDL*, jitData->bodyData->profiledCallApplyCallSiteCount);
}
for (Js::ProfileId i = 0; i < jitData->bodyData->profiledCallApplyCallSiteCount; ++i)
{
const Js::FunctionCodeGenJitTimeData * inlineeJITData = codeGenData->GetCallApplyTargetInlinee(i);
if (inlineeJITData != nullptr)
{
const Js::FunctionCodeGenRuntimeData * inlineeRuntimeData = nullptr;
if (inlineeJITData->GetFunctionInfo()->HasBody())
{
inlineeRuntimeData = isInlinee ? targetRuntimeData->GetCallApplyTargetInlinee(i) : functionBody->GetCallApplyTargetInlineeCodeGenRuntimeData(i);
}
jitData->callApplyTargetInlinees[i] = AnewStructZ(alloc, FunctionJITTimeDataIDL);
BuildJITTimeData(alloc, inlineeJITData, inlineeRuntimeData, jitData->callApplyTargetInlinees[i], true, isForegroundJIT);
}
}
}
jitData->profiledRuntimeData = AnewStructZ(alloc, FunctionJITRuntimeIDL);
if (isInlinee && targetRuntimeData->ClonedInlineCaches()->HasInlineCaches())
{
jitData->profiledRuntimeData->clonedCacheCount = jitData->bodyData->inlineCacheCount;
jitData->profiledRuntimeData->clonedInlineCaches = AnewArray(alloc, intptr_t, jitData->profiledRuntimeData->clonedCacheCount);
for (uint j = 0; j < jitData->bodyData->inlineCacheCount; ++j)
{
jitData->profiledRuntimeData->clonedInlineCaches[j] = (intptr_t)targetRuntimeData->ClonedInlineCaches()->GetInlineCache(j);
}
}
if (jitData->bodyData->inlineCacheCount > 0)
{
jitData->ldFldInlineeCount = jitData->bodyData->inlineCacheCount;
jitData->ldFldInlinees = AnewArrayZ(alloc, FunctionJITTimeDataIDL*, jitData->bodyData->inlineCacheCount);
Field(ObjTypeSpecFldInfo*)* objTypeSpecInfo = codeGenData->GetObjTypeSpecFldInfoArray()->GetInfoArray();
if(objTypeSpecInfo)
{
jitData->objTypeSpecFldInfoCount = jitData->bodyData->inlineCacheCount;
jitData->objTypeSpecFldInfoArray = unsafe_write_barrier_cast<ObjTypeSpecFldIDL**>(objTypeSpecInfo);
}
for (Js::InlineCacheIndex i = 0; i < jitData->bodyData->inlineCacheCount; ++i)
{
const Js::FunctionCodeGenJitTimeData * inlineeJITData = codeGenData->GetLdFldInlinee(i);
const Js::FunctionCodeGenRuntimeData * inlineeRuntimeData = isInlinee ? targetRuntimeData->GetLdFldInlinee(i) : functionBody->GetLdFldInlineeCodeGenRuntimeData(i);
if (inlineeJITData != nullptr)
{
jitData->ldFldInlinees[i] = AnewStructZ(alloc, FunctionJITTimeDataIDL);
BuildJITTimeData(alloc, inlineeJITData, inlineeRuntimeData, jitData->ldFldInlinees[i], true, isForegroundJIT);
}
}
}
if (!isInlinee && codeGenData->GetGlobalObjTypeSpecFldInfoCount() > 0)
{
Field(ObjTypeSpecFldInfo*)* globObjTypeSpecInfo = codeGenData->GetGlobalObjTypeSpecFldInfoArray();
Assert(globObjTypeSpecInfo != nullptr);
jitData->globalObjTypeSpecFldInfoCount = codeGenData->GetGlobalObjTypeSpecFldInfoCount();
jitData->globalObjTypeSpecFldInfoArray = unsafe_write_barrier_cast<ObjTypeSpecFldIDL**>(globObjTypeSpecInfo);
}
const Js::FunctionCodeGenJitTimeData * nextJITData = codeGenData->GetNext();
if (nextJITData != nullptr)
{
// only inlinee should be polymorphic
Assert(isInlinee);
jitData->next = AnewStructZ(alloc, FunctionJITTimeDataIDL);
BuildJITTimeData(alloc, nextJITData, runtimeData, jitData->next, true, isForegroundJIT);
}
}
uint
FunctionJITTimeInfo::GetInlineeCount() const
{
return m_data.inlineeCount;
}
bool
FunctionJITTimeInfo::IsLdFldInlineePresent() const
{
return m_data.ldFldInlineeCount != 0;
}
bool
FunctionJITTimeInfo::HasSharedPropertyGuards() const
{
return m_data.sharedPropGuardCount != 0;
}
bool
FunctionJITTimeInfo::HasSharedPropertyGuard(Js::PropertyId id) const
{
for (uint i = 0; i < m_data.sharedPropGuardCount; ++i)
{
if (m_data.sharedPropertyGuards[i] == id)
{
return true;
}
}
return false;
}
bool FunctionJITTimeInfo::IsJsBuiltInForceInline() const
{
return this->GetBody()->GetSourceContextId() == Js::Constants::JsBuiltInSourceContextId;
}
intptr_t
FunctionJITTimeInfo::GetFunctionInfoAddr() const
{
return m_data.functionInfoAddr;
}
intptr_t
FunctionJITTimeInfo::GetEntryPointInfoAddr() const
{
return m_data.entryPointInfoAddr;
}
intptr_t
FunctionJITTimeInfo::GetWeakFuncRef() const
{
return m_data.weakFuncRef;
}
uint
FunctionJITTimeInfo::GetLocalFunctionId() const
{
return m_data.localFuncId;
}
bool
FunctionJITTimeInfo::IsAggressiveInliningEnabled() const
{
return m_data.isAggressiveInliningEnabled != FALSE;
}
bool
FunctionJITTimeInfo::IsInlined() const
{
return m_data.isInlined != FALSE;
}
const BVFixed *
FunctionJITTimeInfo::GetInlineesBV() const
{
return reinterpret_cast<const BVFixed *>(m_data.inlineesBv);
}
const FunctionJITTimeInfo *
FunctionJITTimeInfo::GetJitTimeDataFromFunctionInfoAddr(intptr_t polyFuncInfo) const
{
const FunctionJITTimeInfo *next = this;
while (next && next->GetFunctionInfoAddr() != polyFuncInfo)
{
next = next->GetNext();
}
return next;
}
const FunctionJITRuntimeInfo *
FunctionJITTimeInfo::GetInlineeForTargetInlineeRuntimeData(const Js::ProfileId profiledCallSiteId, intptr_t inlineeFuncBodyAddr) const
{
const FunctionJITTimeInfo *inlineeData = GetInlinee(profiledCallSiteId);
while (inlineeData && inlineeData->GetBody()->GetAddr() != inlineeFuncBodyAddr)
{
inlineeData = inlineeData->GetNext();
}
__analysis_assume(inlineeData != nullptr);
return inlineeData->GetRuntimeInfo();
}
const FunctionJITRuntimeInfo *
FunctionJITTimeInfo::GetInlineeRuntimeData(const Js::ProfileId profiledCallSiteId) const
{
return GetInlinee(profiledCallSiteId) ? GetInlinee(profiledCallSiteId)->GetRuntimeInfo() : nullptr;
}
const FunctionJITRuntimeInfo *
FunctionJITTimeInfo::GetLdFldInlineeRuntimeData(const Js::InlineCacheIndex inlineCacheIndex) const
{
return GetLdFldInlinee(inlineCacheIndex) ? GetLdFldInlinee(inlineCacheIndex)->GetRuntimeInfo() : nullptr;
}
const FunctionJITRuntimeInfo *
FunctionJITTimeInfo::GetCallbackInlineeRuntimeData(const Js::ProfileId profiledCallSiteId) const
{
return GetCallbackInlinee(profiledCallSiteId) ? GetCallbackInlinee(profiledCallSiteId)->GetRuntimeInfo() : nullptr;
}
const FunctionJITRuntimeInfo *
FunctionJITTimeInfo::GetInlineeForCallbackInlineeRuntimeData(const Js::ProfileId profiledCallSiteId, intptr_t inlineeFuncBodyAddr) const
{
const FunctionJITTimeInfo *inlineeData = GetCallbackInlinee(profiledCallSiteId);
while (inlineeData && inlineeData->GetBody()->GetAddr() != inlineeFuncBodyAddr)
{
inlineeData = inlineeData->GetNext();
}
__analysis_assume(inlineeData != nullptr);
return inlineeData->GetRuntimeInfo();
}
const FunctionJITRuntimeInfo *
FunctionJITTimeInfo::GetCallApplyTargetInlineeRuntimeData(const Js::ProfileId callApplyCallSiteId) const
{
return GetCallApplyTargetInlinee(callApplyCallSiteId) ? GetCallApplyTargetInlinee(callApplyCallSiteId)->GetRuntimeInfo() : nullptr;
}
const FunctionJITRuntimeInfo *
FunctionJITTimeInfo::GetRuntimeInfo() const
{
return reinterpret_cast<const FunctionJITRuntimeInfo*>(m_data.profiledRuntimeData);
}
ObjTypeSpecFldInfo *
FunctionJITTimeInfo::GetObjTypeSpecFldInfo(uint index) const
{
if (m_data.objTypeSpecFldInfoArray == nullptr)
{
return nullptr;
}
AssertOrFailFast(index < m_data.objTypeSpecFldInfoCount);
return reinterpret_cast<ObjTypeSpecFldInfo *>(m_data.objTypeSpecFldInfoArray[index]);
}
ObjTypeSpecFldInfo *
FunctionJITTimeInfo::GetGlobalObjTypeSpecFldInfo(uint index) const
{
AssertOrFailFast(index < m_data.globalObjTypeSpecFldInfoCount);
return reinterpret_cast<ObjTypeSpecFldInfo *>(m_data.globalObjTypeSpecFldInfoArray[index]);
}
uint
FunctionJITTimeInfo::GetGlobalObjTypeSpecFldInfoCount() const
{
return m_data.globalObjTypeSpecFldInfoCount;
}
uint
FunctionJITTimeInfo::GetSourceContextId() const
{
Assert(HasBody());
return GetBody()->GetSourceContextId();
}
const FunctionJITTimeInfo *
FunctionJITTimeInfo::GetCallbackInlinee(Js::ProfileId profileId) const
{
Assert(profileId < m_data.bodyData->profiledCallSiteCount);
if (!m_data.callbackInlinees)
{
return nullptr;
}
AssertOrFailFast(profileId < m_data.inlineeCount);
return reinterpret_cast<const FunctionJITTimeInfo *>(m_data.callbackInlinees[profileId]);
}
const FunctionJITTimeInfo *
FunctionJITTimeInfo::GetCallApplyTargetInlinee(Js::ProfileId callApplyCallSiteId) const
{
if (!m_data.callApplyTargetInlinees)
{
return nullptr;
}
AssertOrFailFast(callApplyCallSiteId < m_data.bodyData->profiledCallApplyCallSiteCount);
return reinterpret_cast<const FunctionJITTimeInfo *>(m_data.callApplyTargetInlinees[callApplyCallSiteId]);
}
const FunctionJITTimeInfo *
FunctionJITTimeInfo::GetLdFldInlinee(Js::InlineCacheIndex inlineCacheIndex) const
{
Assert(inlineCacheIndex < m_data.bodyData->inlineCacheCount);
if (!m_data.ldFldInlinees)
{
return nullptr;
}
AssertOrFailFast(inlineCacheIndex < m_data.ldFldInlineeCount);
return reinterpret_cast<const FunctionJITTimeInfo*>(m_data.ldFldInlinees[inlineCacheIndex]);
}
const FunctionJITTimeInfo *
FunctionJITTimeInfo::GetInlinee(Js::ProfileId profileId) const
{
Assert(profileId < m_data.bodyData->profiledCallSiteCount);
if (!m_data.inlinees)
{
return nullptr;
}
AssertOrFailFast(profileId < m_data.inlineeCount);
auto inlinee = reinterpret_cast<const FunctionJITTimeInfo *>(m_data.inlinees[profileId]);
if (inlinee == nullptr && m_data.inlineesRecursionFlags[profileId])
{
inlinee = this;
}
return inlinee;
}
const FunctionJITTimeInfo *
FunctionJITTimeInfo::GetNext() const
{
return reinterpret_cast<const FunctionJITTimeInfo *>(m_data.next);
}
JITTimeFunctionBody *
FunctionJITTimeInfo::GetBody() const
{
return reinterpret_cast<JITTimeFunctionBody *>(m_data.bodyData);
}
bool
FunctionJITTimeInfo::HasBody() const
{
return m_data.bodyData != nullptr;
}
bool
FunctionJITTimeInfo::IsPolymorphicCallSite(Js::ProfileId profiledCallSiteId) const
{
Assert(profiledCallSiteId < m_data.bodyData->profiledCallSiteCount);
if (!m_data.inlinees)
{
return false;
}
Assert(profiledCallSiteId < m_data.inlineeCount);
return ((FunctionJITTimeDataIDL*)this->GetInlinee(profiledCallSiteId))->next != nullptr;
}
bool
FunctionJITTimeInfo::ForceJITLoopBody() const
{
return
!PHASE_OFF(Js::JITLoopBodyPhase, this) &&
!PHASE_OFF(Js::FullJitPhase, this) &&
!GetBody()->IsGenerator() &&
!GetBody()->HasTry() &&
(
PHASE_FORCE(Js::JITLoopBodyPhase, this)
#ifdef ENABLE_PREJIT
|| Js::Configuration::Global.flags.Prejit
#endif
);
}
char16*
FunctionJITTimeInfo::GetDisplayName() const
{
return GetBody()->GetDisplayName();
}
char16*
FunctionJITTimeInfo::GetDebugNumberSet(wchar(&bufferToWriteTo)[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]) const
{
// (#%u.%u), #%u --> (source file Id . function Id) , function Number
int len = swprintf_s(bufferToWriteTo, MAX_FUNCTION_BODY_DEBUG_STRING_SIZE, _u(" (#%d.%u), #%u"),
(int)GetSourceContextId(), GetLocalFunctionId(), GetBody()->GetFunctionNumber());
Assert(len > 8);
return bufferToWriteTo;
}