Skip to content

Commit

Permalink
update handling of callbackInfo to avoid memory cost for functions wi…
Browse files Browse the repository at this point in the history
…thout callbacks

Rather than store the callback info in the CallSiteInfo, allocate it as needed stored on the function body, similar to how PolymorphicInlineData is handled.
  • Loading branch information
sigatrev committed May 3, 2018
1 parent 967d597 commit 530b02e
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 36 deletions.
49 changes: 47 additions & 2 deletions lib/Backend/JITTimeProfileInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,31 @@ JITTimeProfileInfo::InitializeJITProfileData(
data->profiledCallSiteCount = functionBody->GetProfiledCallSiteCount();
data->callSiteData = reinterpret_cast<CallSiteIDL*>(profileInfo->GetCallSiteInfo());

CompileAssert(sizeof(CallbackInfoIDL) == sizeof(Js::CallbackInfo));
Js::CallbackInfoList * callbackInfoList = functionBody->GetCallbackInfoListWithLock();
if (callbackInfoList == nullptr)
{
data->profiledCallbackCount = 0;
}
else
{
data->profiledCallbackCount = static_cast<Js::ProfileId>(callbackInfoList->Count());
data->callbackData = AnewArrayZ(alloc, CallbackInfoIDL, data->profiledCallbackCount);

size_t callbackInfoIndex = 0;
FOREACH_SLIST_ENTRY(Field(Js::CallbackInfo *), callbackInfo, callbackInfoList)
{
memcpy_s(
&data->callbackData[callbackInfoIndex],
sizeof(CallbackInfoIDL),
callbackInfo,
sizeof(Js::CallbackInfo)
);
++callbackInfoIndex;
}
NEXT_SLIST_ENTRY
}

CompileAssert(sizeof(BVUnitIDL) == sizeof(BVUnit));
data->loopFlags = (BVFixedIDL*)profileInfo->GetLoopFlags();

Expand Down Expand Up @@ -274,8 +299,8 @@ JITTimeProfileInfo::GetLoopFlags(uint loopNum) const
bool
JITTimeProfileInfo::CanInlineCallback(Js::ArgSlot argIndex, Js::ProfileId callSiteId) const
{
Assert(callSiteId < GetProfiledCallSiteCount());
return GetCallSiteInfo()[callSiteId].callbackArgOutInfo.CanInlineCallback(argIndex);
Js::CallbackInfo * callbackInfo = FindCallbackInfo(callSiteId);
return callbackInfo != nullptr && callbackInfo->CanInlineCallback(argIndex);
}

uint16
Expand Down Expand Up @@ -582,6 +607,26 @@ JITTimeProfileInfo::GetCallSiteInfo() const
return reinterpret_cast<Js::CallSiteInfo*>(m_profileData.callSiteData);
}

Js::CallbackInfo *
JITTimeProfileInfo::GetCallbackInfo() const
{
return reinterpret_cast<Js::CallbackInfo*>(m_profileData.callbackData);
}

Js::CallbackInfo *
JITTimeProfileInfo::FindCallbackInfo(Js::ProfileId callSiteId) const
{
Js::CallbackInfo * callbackInfo = GetCallbackInfo();
for (size_t i = 0; i < m_profileData.profiledCallbackCount; ++i)
{
if (callbackInfo[i].callSiteId == callSiteId)
{
return &callbackInfo[i];
}
}
return nullptr;
}

bool
JITTimeProfileInfo::TestFlag(ProfileDataFlags flag) const
{
Expand Down
2 changes: 2 additions & 0 deletions lib/Backend/JITTimeProfileInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ class JITTimeProfileInfo
bool TestFlag(ProfileDataFlags flag) const;

Js::CallSiteInfo * GetCallSiteInfo() const;
Js::CallbackInfo * GetCallbackInfo() const;
Js::CallbackInfo * FindCallbackInfo(Js::ProfileId callSiteId) const;

ProfileDataIDL m_profileData;
};
9 changes: 5 additions & 4 deletions lib/JITIDL/JITTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,22 +205,20 @@ typedef struct BVSparseNodeIDL
__int64 data;
} BVSparseNodeIDL;

typedef struct CallbackArgOutInfoIDL
typedef struct CallbackInfoIDL
{
byte argInfoButs;
IDL_PAD1(0)
IDL_PAD2(1)
unsigned int sourceId;
unsigned int functionId;
} CallbackArgOutInfoIDL;
} CallbackInfoIDL;

typedef struct CallSiteIDL
{
unsigned short bitFields;
unsigned short returnType;
unsigned int ldFldInlineCacheId;
CallbackArgOutInfoIDL callbackArgOutInfoIDL;
X64_PAD4(0)
unsigned int sourceId;
unsigned int functionId;
} CallSiteIDL;
Expand Down Expand Up @@ -287,6 +285,7 @@ typedef struct ProfileDataIDL

unsigned short profiledSlotCount;
unsigned short profiledCallSiteCount;
unsigned short profiledCallbackCount;

unsigned short profiledReturnTypeCount;
unsigned short profiledDivOrRemCount;
Expand All @@ -313,6 +312,8 @@ typedef struct ProfileDataIDL

IDL_DEF([size_is(profiledCallSiteCount)]) CallSiteIDL * callSiteData;

IDL_DEF([size_is(profiledCallbackCount)]) CallbackInfoIDL * callbackData;

IDL_DEF([size_is(profiledReturnTypeCount)]) unsigned short * returnTypeData;

IDL_DEF([size_is(profiledDivOrRemCount)]) unsigned short * divideTypeInfo;
Expand Down
5 changes: 5 additions & 0 deletions lib/Runtime/Base/FunctionBody.h
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,7 @@ namespace Js
ForInCacheArray = 22,
SlotIdInCachedScopeToNestedIndexArray = 23,
CodeGenCallbackRuntimeData = 24,
CallbackArgOutInfoList = 25,

Max,
Invalid = 0xff
Expand Down Expand Up @@ -2998,6 +2999,10 @@ namespace Js
#if ENABLE_NATIVE_CODEGEN
void SetPolymorphicCallSiteInfoHead(PolymorphicCallSiteInfo *polyCallSiteInfo) { this->SetAuxPtr(AuxPointerType::PolymorphicCallSiteInfoHead, polyCallSiteInfo); }
PolymorphicCallSiteInfo * GetPolymorphicCallSiteInfoHead() { return static_cast<PolymorphicCallSiteInfo *>(this->GetAuxPtr(AuxPointerType::PolymorphicCallSiteInfoHead)); }

void SetCallbackInfoList(CallbackInfoList * callbackInfoList) { this->SetAuxPtr(AuxPointerType::CallbackArgOutInfoList, callbackInfoList); }
CallbackInfoList * GetCallbackInfoList() { return static_cast<CallbackInfoList *>(this->GetAuxPtr(AuxPointerType::CallbackArgOutInfoList)); }
CallbackInfoList * GetCallbackInfoListWithLock() { return static_cast<CallbackInfoList *>(this->GetAuxPtrWithLock(AuxPointerType::CallbackArgOutInfoList)); }
#endif

FunctionBodyPolymorphicInlineCache * GetPolymorphicInlineCachesHead() { return static_cast<FunctionBodyPolymorphicInlineCache *>(this->GetAuxPtr(AuxPointerType::PolymorphicInlineCachesHead)); }
Expand Down
85 changes: 62 additions & 23 deletions lib/Runtime/Language/DynamicProfileInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ namespace Js
}
for (ProfileId i = 0; i < functionBody->GetProfiledCallSiteCount(); ++i)
{
callSiteInfo[i].callbackArgOutInfo.canInlineCallback = true;
callSiteInfo[i].returnType = ValueType::Uninitialized;
callSiteInfo[i].u.functionData.sourceId = NoSourceId;
}
Expand Down Expand Up @@ -433,44 +432,41 @@ namespace Js
return;
}

CallbackArgOutInfo & callbackInfo = callSiteInfo[callSiteId].callbackArgOutInfo;
if (arg != nullptr && RecyclableObject::Is(arg) && JavascriptFunction::Is(arg))
{
if (callbackInfo.canInlineCallback)
CallbackInfo * callbackInfo = EnsureCallbackInfo(functionBody, callSiteId);
if (callbackInfo->sourceId == NoSourceId)
{
if (callbackInfo.hasCallbackArgument && argNum != callbackInfo.argNumber)
JavascriptFunction * callback = JavascriptFunction::UnsafeFromVar(arg);
GetSourceAndFunctionId(functionBody, callback->GetFunctionInfo(), callback, &callbackInfo->sourceId, &callbackInfo->functionId);
callbackInfo->argNumber = argNum;
}
else if (callbackInfo->canInlineCallback)
{
if (argNum != callbackInfo->argNumber)
{
callbackInfo.canInlineCallback = false;
callbackInfo->canInlineCallback = false;
}
else if (!callbackInfo.isPolymorphic)
else if (!callbackInfo->isPolymorphic)
{
Js::SourceId sourceId;
Js::LocalFunctionId functionId;
JavascriptFunction * callback = JavascriptFunction::UnsafeFromVar(arg);
GetSourceAndFunctionId(functionBody, callback->GetFunctionInfo(), callback, &sourceId, &functionId);

if (callbackInfo.hasCallbackArgument)
{
if (sourceId != callbackInfo.sourceId || functionId != callbackInfo.functionId)
{
callbackInfo.isPolymorphic = true;
}
}
else
if (sourceId != callbackInfo->sourceId || functionId != callbackInfo->functionId)
{
callbackInfo.hasCallbackArgument = true;
callbackInfo.sourceId = sourceId;
callbackInfo.functionId = functionId;
callbackInfo.argNumber = argNum;
callbackInfo->isPolymorphic = true;
}
}
}
}
else
{
if (callbackInfo.hasCallbackArgument && callbackInfo.argNumber == argNum)
CallbackInfo * callbackInfo = FindCallbackInfo(functionBody, callSiteId);
if (callbackInfo != nullptr && callbackInfo->argNumber == argNum)
{
callbackInfo.canInlineCallback = false;
callbackInfo->canInlineCallback = false;
}

if (TaggedInt::Is(arg) && regSlot < functionBody->GetConstantCount())
Expand All @@ -480,6 +476,49 @@ namespace Js
}
}

CallbackInfo * DynamicProfileInfo::FindCallbackInfo(FunctionBody * funcBody, ProfileId callSiteId)
{
CallbackInfoList * list = funcBody->GetCallbackInfoList();
if (list == nullptr)
{
return nullptr;
}

FOREACH_SLIST_ENTRY(Field(CallbackInfo *), callbackInfo, list)
{
if (callbackInfo->callSiteId == callSiteId)
{
return callbackInfo;
}
}
NEXT_SLIST_ENTRY;

return nullptr;
}

CallbackInfo * DynamicProfileInfo::EnsureCallbackInfo(FunctionBody * funcBody, ProfileId callSiteId)
{
CallbackInfoList * list = funcBody->GetCallbackInfoList();
if (list == nullptr)
{
Recycler * recycler = funcBody->GetScriptContext()->GetRecycler();
list = RecyclerNew(recycler, CallbackInfoList, recycler);
funcBody->SetCallbackInfoList(list);
}

CallbackInfo * info = FindCallbackInfo(funcBody, callSiteId);
if (info == nullptr)
{
info = RecyclerNewStructZ(funcBody->GetScriptContext()->GetRecycler(), CallbackInfo);
info->callSiteId = callSiteId;
info->sourceId = NoSourceId;
info->canInlineCallback = true;
list->Prepend(info);
}

return info;
}

uint16 DynamicProfileInfo::GetConstantArgInfo(ProfileId callSiteId)
{
return callSiteInfo[callSiteId].isArgConstant;
Expand Down Expand Up @@ -1028,13 +1067,13 @@ namespace Js
Assert(callSiteId < callSiteCount);
Assert(functionBody->IsJsBuiltInCode() || functionBody->IsPublicLibraryCode() || HasCallSiteInfo(functionBody));

CallbackArgOutInfo & callbackInfo = callSiteInfo[callSiteId].callbackArgOutInfo;
if (!callbackInfo.canInlineCallback || !callbackInfo.hasCallbackArgument || callbackInfo.isPolymorphic)
CallbackInfo * callbackInfo = FindCallbackInfo(functionBody, callSiteId);
if (callbackInfo == nullptr || !callbackInfo->canInlineCallback || callbackInfo->isPolymorphic)
{
return nullptr;
}

return GetFunctionInfo(functionBody, callbackInfo.sourceId, callbackInfo.functionId);
return GetFunctionInfo(functionBody, callbackInfo->sourceId, callbackInfo->functionId);
}

uint DynamicProfileInfo::GetLdFldCacheIndexFromCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId)
Expand Down
16 changes: 9 additions & 7 deletions lib/Runtime/Language/DynamicProfileInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,30 +84,31 @@ namespace Js
}
};

struct CallbackArgOutInfo
struct CallbackInfo
{
bool CanInlineCallback(Js::ArgSlot argIndex)
{
return canInlineCallback && hasCallbackArgument && argNumber == argIndex;
return canInlineCallback && argNumber == argIndex;
}

// False if there is more than one ArgIn that is a function object or a function object with arg number greater than MaxInlineeArgoutCount
Field(uint8) canInlineCallback : 1;

// True if there is at least one ArgIn that is a function object.
Field(uint8) hasCallbackArgument : 1;

Field(uint8) isConstructor : 1;
Field(uint8) isPolymorphic : 1;

// Used to correlate from callee's ArgIn to this ArgOut
Field(uint8) argNumber : 5;
static_assert(Js::InlineeCallInfo::MaxInlineeArgoutCount < (1 << 5), "Ensure CallbackInfo::argNumber is large enough to hold all inline arguments");

static_assert(Js::InlineeCallInfo::MaxInlineeArgoutCount < (1 << 5), "Ensure CallbackArgOutInfo::argNumber is large enough to hold all inline arguments");
Field(uint16) callSiteId;

Field(Js::SourceId) sourceId;
Field(Js::LocalFunctionId) functionId;
};

using CallbackInfoList = SList<CallbackInfo*, Recycler, RealCount>;

struct CallSiteInfo
{
Field(ValueType) returnType;
Expand All @@ -116,7 +117,6 @@ namespace Js
Field(uint16) dontInline : 1;
Field(uint16) isPolymorphic : 1;
Field(InlineCacheIndex) ldFldInlineCacheId;
Field(CallbackArgOutInfo) callbackArgOutInfo;
union _u_type
{
struct
Expand Down Expand Up @@ -621,6 +621,8 @@ namespace Js

static void DumpLoopInfo(FunctionBody *fbody);
#endif
CallbackInfo * FindCallbackInfo(FunctionBody * funcBody, ProfileId callSiteId);
CallbackInfo * EnsureCallbackInfo(FunctionBody * funcBody, ProfileId callSiteId);

bool IsPolymorphicCallSite(Js::LocalFunctionId curFunctionId, Js::SourceId curSourceId, Js::LocalFunctionId oldFunctionId, Js::SourceId oldSourceId);
void CreatePolymorphicDynamicProfileCallSiteInfo(FunctionBody * funcBody, ProfileId callSiteId, Js::LocalFunctionId functionId, Js::LocalFunctionId oldFunctionId, Js::SourceId sourceId, Js::SourceId oldSourceId);
Expand Down

0 comments on commit 530b02e

Please sign in to comment.