diff --git a/lib/Backend/JITTimeProfileInfo.cpp b/lib/Backend/JITTimeProfileInfo.cpp index 9ed0f584dce..e42ddd9743a 100644 --- a/lib/Backend/JITTimeProfileInfo.cpp +++ b/lib/Backend/JITTimeProfileInfo.cpp @@ -84,6 +84,31 @@ JITTimeProfileInfo::InitializeJITProfileData( data->profiledCallSiteCount = functionBody->GetProfiledCallSiteCount(); data->callSiteData = reinterpret_cast(profileInfo->GetCallSiteInfo()); + CompileAssert(sizeof(CallbackInfoIDL) == sizeof(Js::CallbackInfo)); + Js::CallbackInfoList * callbackInfoList = functionBody->GetCallbackInfoListWithLock(); + if (callbackInfoList == nullptr) + { + data->profiledCallbackCount = 0; + } + else + { + data->profiledCallbackCount = static_cast(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(); @@ -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 @@ -582,6 +607,26 @@ JITTimeProfileInfo::GetCallSiteInfo() const return reinterpret_cast(m_profileData.callSiteData); } +Js::CallbackInfo * +JITTimeProfileInfo::GetCallbackInfo() const +{ + return reinterpret_cast(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 { diff --git a/lib/Backend/JITTimeProfileInfo.h b/lib/Backend/JITTimeProfileInfo.h index da446e6eea1..55e89b8b989 100644 --- a/lib/Backend/JITTimeProfileInfo.h +++ b/lib/Backend/JITTimeProfileInfo.h @@ -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; }; diff --git a/lib/JITIDL/JITTypes.h b/lib/JITIDL/JITTypes.h index 56593566d8b..9ef318cde42 100644 --- a/lib/JITIDL/JITTypes.h +++ b/lib/JITIDL/JITTypes.h @@ -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; @@ -287,6 +285,7 @@ typedef struct ProfileDataIDL unsigned short profiledSlotCount; unsigned short profiledCallSiteCount; + unsigned short profiledCallbackCount; unsigned short profiledReturnTypeCount; unsigned short profiledDivOrRemCount; @@ -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; diff --git a/lib/Runtime/Base/FunctionBody.h b/lib/Runtime/Base/FunctionBody.h index 91bef00a954..07b1fa3df6d 100644 --- a/lib/Runtime/Base/FunctionBody.h +++ b/lib/Runtime/Base/FunctionBody.h @@ -1196,6 +1196,7 @@ namespace Js ForInCacheArray = 22, SlotIdInCachedScopeToNestedIndexArray = 23, CodeGenCallbackRuntimeData = 24, + CallbackArgOutInfoList = 25, Max, Invalid = 0xff @@ -2998,6 +2999,10 @@ namespace Js #if ENABLE_NATIVE_CODEGEN void SetPolymorphicCallSiteInfoHead(PolymorphicCallSiteInfo *polyCallSiteInfo) { this->SetAuxPtr(AuxPointerType::PolymorphicCallSiteInfoHead, polyCallSiteInfo); } PolymorphicCallSiteInfo * GetPolymorphicCallSiteInfoHead() { return static_cast(this->GetAuxPtr(AuxPointerType::PolymorphicCallSiteInfoHead)); } + + void SetCallbackInfoList(CallbackInfoList * callbackInfoList) { this->SetAuxPtr(AuxPointerType::CallbackArgOutInfoList, callbackInfoList); } + CallbackInfoList * GetCallbackInfoList() { return static_cast(this->GetAuxPtr(AuxPointerType::CallbackArgOutInfoList)); } + CallbackInfoList * GetCallbackInfoListWithLock() { return static_cast(this->GetAuxPtrWithLock(AuxPointerType::CallbackArgOutInfoList)); } #endif FunctionBodyPolymorphicInlineCache * GetPolymorphicInlineCachesHead() { return static_cast(this->GetAuxPtr(AuxPointerType::PolymorphicInlineCachesHead)); } diff --git a/lib/Runtime/Language/DynamicProfileInfo.cpp b/lib/Runtime/Language/DynamicProfileInfo.cpp index 1adfa0a6ba1..0c8b7ceed8e 100644 --- a/lib/Runtime/Language/DynamicProfileInfo.cpp +++ b/lib/Runtime/Language/DynamicProfileInfo.cpp @@ -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; } @@ -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()) @@ -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; @@ -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) diff --git a/lib/Runtime/Language/DynamicProfileInfo.h b/lib/Runtime/Language/DynamicProfileInfo.h index 92deffe17e6..b4549715eb2 100644 --- a/lib/Runtime/Language/DynamicProfileInfo.h +++ b/lib/Runtime/Language/DynamicProfileInfo.h @@ -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; + struct CallSiteInfo { Field(ValueType) returnType; @@ -116,7 +117,6 @@ namespace Js Field(uint16) dontInline : 1; Field(uint16) isPolymorphic : 1; Field(InlineCacheIndex) ldFldInlineCacheId; - Field(CallbackArgOutInfo) callbackArgOutInfo; union _u_type { struct @@ -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);