blob: 98851f064999270107a77472c24aa14af64903b5 [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
// DisableJit-TODO
#if ENABLE_PROFILE_INFO
#ifdef DYNAMIC_PROFILE_MUTATOR
class DynamicProfileMutatorImpl;
#endif
#define PolymorphicInlineCacheUtilizationMinValue 0
#define PolymorphicInlineCacheUtilizationMaxValue 0xFF
#define PolymorphicInlineCacheUtilizationThreshold 0x80
#define PolymorphicInlineCacheUtilizationIncrement 10
#define PolymorphicInlineCacheUtilizationDecrement 1
namespace IR
{
enum BailOutKind : uint
{
#define BAIL_OUT_KIND_LAST(n) n
#define BAIL_OUT_KIND(n, ...) BAIL_OUT_KIND_LAST(n),
#define BAIL_OUT_KIND_VALUE_LAST(n, v) n = v
#define BAIL_OUT_KIND_VALUE(n, v) BAIL_OUT_KIND_VALUE_LAST(n, v),
#include "BailOutKind.h"
#undef BAIL_OUT_KIND_LAST
};
ENUM_CLASS_HELPERS(BailOutKind, uint);
CompileAssert(BailOutKind::BailOutKindEnd < BailOutKind::BailOutKindBitsStart);
bool IsTypeCheckBailOutKind(BailOutKind kind);
bool IsEquivalentTypeCheckBailOutKind(BailOutKind kind);
BailOutKind EquivalentToMonoTypeCheckBailOutKind(BailOutKind kind);
}
#if ENABLE_DEBUG_CONFIG_OPTIONS || defined(REJIT_STATS)
const char *GetBailOutKindName(IR::BailOutKind kind);
bool IsValidBailOutKindAndBits(IR::BailOutKind bailOutKind);
#endif
namespace Js
{
enum CacheType : byte;
enum SlotType : byte;
struct PolymorphicCallSiteInfo;
// Information about dynamic profile information loaded from cache.
// Used to verify whether the loaded information matches the paired function body.
class DynamicProfileFunctionInfo
{
public:
Field(Js::ArgSlot) paramInfoCount;
Field(ProfileId) ldElemInfoCount;
Field(ProfileId) stElemInfoCount;
Field(ProfileId) arrayCallSiteCount;
Field(ProfileId) slotInfoCount;
Field(ProfileId) callSiteInfoCount;
Field(ProfileId) returnTypeInfoCount;
Field(ProfileId) divCount;
Field(ProfileId) switchCount;
Field(uint) loopCount;
Field(uint) fldInfoCount;
};
enum ThisType : BYTE
{
ThisType_Unknown = 0,
ThisType_Simple,
ThisType_Mapped
};
struct ThisInfo
{
Field(ValueType) valueType;
Field(ThisType) thisType;
ThisInfo() : thisType(ThisType_Unknown)
{
}
};
struct CallSiteInfo
{
Field(ValueType) returnType;
Field(uint16) isArgConstant : 13;
Field(uint16) isConstructorCall : 1;
Field(uint16) dontInline : 1;
Field(uint16) isPolymorphic : 1;
Field(InlineCacheIndex) ldFldInlineCacheId;
union _u_type
{
struct
{
Field(Js::SourceId) sourceId;
Field(Js::LocalFunctionId) functionId;
} functionData;
// As of now polymorphic info is allocated only if the source Id is current
Field(PolymorphicCallSiteInfo*) polymorphicCallSiteInfo;
_u_type() {}
} u;
};
// TODO: include ImplicitCallFlags in this structure
struct LoopFlags
{
// maintain the bits and the enum at the same time, it must match
bool isInterpreted : 1;
bool memopMinCountReached : 1;
enum
{
INTERPRETED,
MEMOP_MIN_COUNT_FOUND,
COUNT
};
LoopFlags() :
isInterpreted(false),
memopMinCountReached(false)
{
CompileAssert((sizeof(LoopFlags) * 8) >= LoopFlags::COUNT);
}
// Right now supports up to 8 bits.
typedef byte LoopFlags_t;
LoopFlags(uint64 flags)
{
Assert(flags >> LoopFlags::COUNT == 0);
LoopFlags_t* thisFlags = (LoopFlags_t *)this;
CompileAssert(sizeof(LoopFlags_t) == sizeof(LoopFlags));
*thisFlags = (LoopFlags_t)flags;
}
};
enum FldInfoFlags : BYTE
{
FldInfo_NoInfo = 0x00,
FldInfo_FromLocal = 0x01,
FldInfo_FromProto = 0x02,
FldInfo_FromLocalWithoutProperty = 0x04,
FldInfo_FromAccessor = 0x08,
FldInfo_Polymorphic = 0x10,
FldInfo_FromInlineSlots = 0x20,
FldInfo_FromAuxSlots = 0x40,
FldInfo_InlineCandidate = 0x80
};
struct FldInfo
{
typedef struct { ValueType::TSize f1; byte f2; byte f3; } TSize;
ValueType valueType;
FldInfoFlags flags;
byte polymorphicInlineCacheUtilization;
bool ShouldUsePolymorphicInlineCache()
{
#if DBG
if (PHASE_FORCE1(PolymorphicInlineCachePhase))
{
return true;
}
#endif
return polymorphicInlineCacheUtilization > PolymorphicInlineCacheUtilizationThreshold;
}
bool WasLdFldProfiled() const
{
return !valueType.IsUninitialized();
}
static uint32 GetOffsetOfFlags() { return offsetof(FldInfo, flags); }
};
CompileAssert(sizeof(FldInfo::TSize) == sizeof(FldInfo));
struct LdElemInfo
{
ValueType arrayType;
ValueType elemType;
union
{
struct
{
bool wasProfiled : 1;
bool neededHelperCall : 1;
};
byte bits;
};
LdElemInfo() : bits(0)
{
wasProfiled = true;
}
void Merge(const LdElemInfo &other)
{
arrayType = arrayType.Merge(other.arrayType);
elemType = elemType.Merge(other.elemType);
bits |= other.bits;
}
ValueType GetArrayType() const
{
return arrayType;
}
ValueType GetElementType() const
{
return elemType;
}
bool WasProfiled() const
{
return wasProfiled;
}
bool LikelyNeedsHelperCall() const
{
return neededHelperCall;
}
};
struct StElemInfo
{
ValueType arrayType;
union
{
struct
{
bool wasProfiled : 1;
bool createdMissingValue : 1;
bool filledMissingValue : 1;
bool neededHelperCall : 1;
bool storedOutsideHeadSegmentBounds : 1;
bool storedOutsideArrayBounds : 1;
};
byte bits;
};
StElemInfo() : bits(0)
{
wasProfiled = true;
}
void Merge(const StElemInfo &other)
{
arrayType = arrayType.Merge(other.arrayType);
bits |= other.bits;
}
ValueType GetArrayType() const
{
return arrayType;
}
bool WasProfiled() const
{
return wasProfiled;
}
bool LikelyCreatesMissingValue() const
{
return createdMissingValue;
}
bool LikelyFillsMissingValue() const
{
return filledMissingValue;
}
bool LikelyNeedsHelperCall() const
{
return createdMissingValue || filledMissingValue || neededHelperCall || storedOutsideHeadSegmentBounds;
}
bool LikelyStoresOutsideHeadSegmentBounds() const
{
return createdMissingValue || storedOutsideHeadSegmentBounds;
}
bool LikelyStoresOutsideArrayBounds() const
{
return storedOutsideArrayBounds;
}
};
struct ArrayCallSiteInfo
{
union {
struct {
byte isNotNativeInt : 1;
byte isNotNativeFloat : 1;
#if ENABLE_COPYONACCESS_ARRAY
byte isNotCopyOnAccessArray : 1;
byte copyOnAccessArrayCacheIndex : 5;
#endif
};
byte bits;
};
#if DBG
uint functionNumber;
ProfileId callSiteNumber;
#endif
bool IsNativeIntArray() const { return !(bits & NotNativeIntBit) && !PHASE_OFF1(NativeArrayPhase); }
bool IsNativeFloatArray() const { return !(bits & NotNativeFloatBit) && !PHASE_OFF1(NativeArrayPhase); }
bool IsNativeArray() const { return IsNativeFloatArray(); }
void SetIsNotNativeIntArray();
void SetIsNotNativeFloatArray();
void SetIsNotNativeArray();
static uint32 GetOffsetOfBits() { return offsetof(ArrayCallSiteInfo, bits); }
static byte const NotNativeIntBit = 1;
static byte const NotNativeFloatBit = 2;
};
class DynamicProfileInfo;
typedef SListBase<DynamicProfileInfo*, Recycler> DynamicProfileInfoList;
class DynamicProfileInfo
{
public:
static DynamicProfileInfo* New(Recycler* recycler, FunctionBody* functionBody, bool persistsAcrossScriptContexts = false);
void Initialize(FunctionBody *const functionBody);
public:
static bool IsEnabledForAtLeastOneFunction(const ScriptContext *const scriptContext);
static bool IsEnabled(const FunctionBody *const functionBody);
private:
static bool IsEnabled_OptionalFunctionBody(const FunctionBody *const functionBody, const ScriptContext *const scriptContext);
public:
static bool IsEnabledForAtLeastOneFunction(const Js::Phase phase, const ScriptContext *const scriptContext);
static bool IsEnabled(const Js::Phase phase, const FunctionBody *const functionBody);
private:
static bool IsEnabled_OptionalFunctionBody(const Js::Phase phase, const FunctionBody *const functionBody, const ScriptContext *const scriptContext);
public:
static bool EnableImplicitCallFlags(const FunctionBody *const functionBody);
static Var EnsureDynamicProfileInfoThunk(RecyclableObject * function, CallInfo callInfo, ...);
#ifdef DYNAMIC_PROFILE_STORAGE
bool HasFunctionBody() const { return hasFunctionBody; }
FunctionBody * GetFunctionBody() const { Assert(hasFunctionBody); return functionBody; }
#endif
void RecordElementLoad(FunctionBody* functionBody, ProfileId ldElemId, const LdElemInfo& info);
void RecordElementLoadAsProfiled(FunctionBody *const functionBody, const ProfileId ldElemId);
const LdElemInfo *GetLdElemInfo() const { return ldElemInfo; }
void RecordElementStore(FunctionBody* functionBody, ProfileId stElemId, const StElemInfo& info);
void RecordElementStoreAsProfiled(FunctionBody *const functionBody, const ProfileId stElemId);
const StElemInfo *GetStElemInfo() const { return stElemInfo; }
ArrayCallSiteInfo *GetArrayCallSiteInfo(FunctionBody *functionBody, ProfileId index) const;
ArrayCallSiteInfo *GetArrayCallSiteInfo() const { return arrayCallSiteInfo; }
void RecordFieldAccess(FunctionBody* functionBody, uint fieldAccessId, Var object, FldInfoFlags flags);
void RecordPolymorphicFieldAccess(FunctionBody *functionBody, uint fieldAccessid);
bool HasPolymorphicFldAccess() const { return bits.hasPolymorphicFldAccess; }
FldInfo * GetFldInfo(FunctionBody* functionBody, uint fieldAccessId) const;
FldInfo * GetFldInfo() const { return fldInfo; }
void RecordSlotLoad(FunctionBody* functionBody, ProfileId slotLoadId, Var object);
ValueType GetSlotLoad(FunctionBody* functionBody, ProfileId slotLoadId) const;
ValueType * GetSlotInfo() const { return slotInfo; }
void RecordThisInfo(Var object, ThisType thisType);
ThisInfo GetThisInfo() const;
void RecordDivideResultType(FunctionBody* body, ProfileId divideId, Var object);
ValueType GetDivideResultType(FunctionBody* body, ProfileId divideId) const;
ValueType * GetDivideTypeInfo() const { return divideTypeInfo; }
void RecordModulusOpType(FunctionBody* body, ProfileId profileId, bool isModByPowerOf2);
bool IsModulusOpByPowerOf2(FunctionBody* body, ProfileId profileId) const;
void RecordSwitchType(FunctionBody* body, ProfileId switchId, Var object);
ValueType GetSwitchType(FunctionBody* body, ProfileId switchId) const;
ValueType * GetSwitchTypeInfo() const { return switchTypeInfo; }
void RecordCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId, FunctionInfo * calleeFunctionInfo, JavascriptFunction* calleeFunction, ArgSlot actualArgCount, bool isConstructorCall, InlineCacheIndex ldFldInlineCacheId = Js::Constants::NoInlineCacheIndex);
void RecordConstParameterAtCallSite(ProfileId callSiteId, int argNum);
static bool HasCallSiteInfo(FunctionBody* functionBody);
bool HasCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId); // Does a particular callsite have ProfileInfo?
FunctionInfo * GetCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId, bool *isConstructorCall, bool *isPolymorphicCall);
CallSiteInfo * GetCallSiteInfo() const { return callSiteInfo; }
uint16 GetConstantArgInfo(ProfileId callSiteId);
uint GetLdFldCacheIndexFromCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId);
bool GetPolymorphicCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId, bool *isConstructorCall, __inout_ecount(functionBodyArrayLength) FunctionBody** functionBodyArray, uint functionBodyArrayLength);
bool RecordLdFldCallSiteInfo(FunctionBody* functionBody, RecyclableObject* callee, bool callApplyTarget);
bool HasLdFldCallSiteInfo() const;
void RecordReturnTypeOnCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId, Var object);
void RecordReturnType(FunctionBody* functionBody, ProfileId callSiteId, Var object);
ValueType GetReturnType(FunctionBody* functionBody, Js::OpCode opcode, ProfileId callSiteId) const;
ValueType * GetReturnTypeInfo() const { return returnTypeInfo; }
void RecordParameterInfo(FunctionBody* functionBody, ArgSlot index, Var object);
ValueType GetParameterInfo(FunctionBody* functionBody, ArgSlot index) const;
ValueType * GetParameterInfo() const { return parameterInfo; }
void RecordLoopImplicitCallFlags(FunctionBody* functionBody, uint loopNum, ImplicitCallFlags flags);
ImplicitCallFlags GetLoopImplicitCallFlags(FunctionBody* functionBody, uint loopNum) const;
ImplicitCallFlags * GetLoopImplicitCallFlags() const { return loopImplicitCallFlags; }
void RecordImplicitCallFlags(ImplicitCallFlags flags);
ImplicitCallFlags GetImplicitCallFlags() const;
static void Save(ScriptContext * scriptContext);
void UpdateFunctionInfo(FunctionBody* functionBody, Recycler* allocator);
void ResetAllPolymorphicCallSiteInfo();
bool CallSiteHasProfileData(ProfileId callSiteId)
{
return this->callSiteInfo[callSiteId].isPolymorphic
|| this->callSiteInfo[callSiteId].u.functionData.sourceId != NoSourceId
|| this->callSiteInfo[callSiteId].dontInline;
}
static bool IsProfiledCallOp(OpCode op);
static bool IsProfiledReturnTypeOp(OpCode op);
static FldInfoFlags FldInfoFlagsFromCacheType(CacheType cacheType);
static FldInfoFlags FldInfoFlagsFromSlotType(SlotType slotType);
static FldInfoFlags MergeFldInfoFlags(FldInfoFlags oldFlags, FldInfoFlags newFlags);
const static uint maxPolymorphicInliningSize = 4;
#if DBG_DUMP
static void DumpScriptContext(ScriptContext * scriptContext);
static char16 const * GetImplicitCallFlagsString(ImplicitCallFlags flags);
#endif
#ifdef RUNTIME_DATA_COLLECTION
static void DumpScriptContextToFile(ScriptContext * scriptContext);
#endif
#if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
static bool NeedProfileInfoList();
#endif
#ifdef DYNAMIC_PROFILE_MUTATOR
friend class DynamicProfileMutatorImpl;
#endif
#if JS_PROFILE_DATA_INTERFACE
friend class ProfileDataObject;
#endif
private:
// Have the dynamicProfileFunctionInfo after loaded from cache.
// Replaced with the function body it is verified and matched (See DynamicProfileInfo::MatchFunctionBody)
Field(DynamicProfileFunctionInfo *) dynamicProfileFunctionInfo;
Field(CallSiteInfo *) callSiteInfo;
Field(ValueType *) returnTypeInfo; // return type of calls for non inline call sites
Field(ValueType *) divideTypeInfo;
Field(ValueType *) switchTypeInfo;
Field(LdElemInfo *) ldElemInfo;
Field(StElemInfo *) stElemInfo;
Field(ArrayCallSiteInfo *) arrayCallSiteInfo;
Field(ValueType *) parameterInfo;
Field(FldInfo *) fldInfo;
Field(ValueType *) slotInfo;
Field(ImplicitCallFlags *) loopImplicitCallFlags;
Field(ImplicitCallFlags) implicitCallFlags;
Field(BVFixed*) loopFlags;
Field(ThisInfo) thisInfo;
// TODO (jedmiad): Consider storing a pair of property ID bit vectors indicating which properties are
// known to be non-fixed or non-equivalent. We could turn these on if we bailed out of fixed field type
// checks and equivalent type checks in a way that indicates one of these failures as opposed to type
// mismatch.
struct Bits
{
Field(bool) disableAggressiveIntTypeSpec : 1;
Field(bool) disableAggressiveIntTypeSpec_jitLoopBody : 1;
Field(bool) disableAggressiveMulIntTypeSpec : 1;
Field(bool) disableAggressiveMulIntTypeSpec_jitLoopBody : 1;
Field(bool) disableDivIntTypeSpec : 1;
Field(bool) disableDivIntTypeSpec_jitLoopBody : 1;
Field(bool) disableLossyIntTypeSpec : 1;
// TODO: put this flag in LoopFlags if we can find a reliable way to determine the loopNumber in bailout for a hoisted instr
Field(bool) disableMemOp : 1;
Field(bool) disableTrackCompoundedIntOverflow : 1;
Field(bool) disableFloatTypeSpec : 1;
Field(bool) disableCheckThis : 1;
Field(bool) disableArrayCheckHoist : 1;
Field(bool) disableArrayCheckHoist_jitLoopBody : 1;
Field(bool) disableArrayMissingValueCheckHoist : 1;
Field(bool) disableArrayMissingValueCheckHoist_jitLoopBody : 1;
Field(bool) disableJsArraySegmentHoist : 1;
Field(bool) disableJsArraySegmentHoist_jitLoopBody : 1;
Field(bool) disableArrayLengthHoist : 1;
Field(bool) disableArrayLengthHoist_jitLoopBody : 1;
Field(bool) disableTypedArrayTypeSpec : 1;
Field(bool) disableTypedArrayTypeSpec_jitLoopBody : 1;
Field(bool) disableLdLenIntSpec : 1;
Field(bool) disableBoundCheckHoist : 1;
Field(bool) disableBoundCheckHoist_jitLoopBody : 1;
Field(bool) disableLoopCountBasedBoundCheckHoist : 1;
Field(bool) disableLoopCountBasedBoundCheckHoist_jitLoopBody : 1;
Field(bool) hasPolymorphicFldAccess : 1;
Field(bool) hasLdFldCallSite : 1; // getters, setters, .apply (possibly .call too in future)
Field(bool) disableFloorInlining : 1;
Field(bool) disableNoProfileBailouts : 1;
Field(bool) disableSwitchOpt : 1;
Field(bool) disableEquivalentObjTypeSpec : 1;
Field(bool) disableObjTypeSpec_jitLoopBody : 1;
Field(bool) disablePowIntIntTypeSpec : 1;
Field(bool) disableLoopImplicitCallInfo : 1;
Field(bool) disableStackArgOpt : 1;
Field(bool) disableTagCheck : 1;
Field(bool) disableOptimizeTryFinally : 1;
};
Field(Bits) bits;
Field(uint32) m_recursiveInlineInfo; // Bit is set for each callsites where the function is called recursively
Field(uint32) polymorphicCacheState;
Field(uint32) bailOutOffsetForLastRejit;
Field(bool) hasFunctionBody; // this is likely 1, try avoid 4-byte GC force reference
Field(BYTE) currentInlinerVersion; // Used to detect when inlining profile changes
Field(uint16) rejitCount;
#if DBG
Field(bool) persistsAcrossScriptContexts;
#endif
static JavascriptMethod EnsureDynamicProfileInfo(Js::ScriptFunction * function);
#if DBG_DUMP
static void DumpList(DynamicProfileInfoList * profileInfoList, ArenaAllocator * dynamicProfileInfoAllocator);
static void DumpProfiledValue(char16 const * name, uint * value, uint count);
static void DumpProfiledValue(char16 const * name, ValueType * value, uint count);
static void DumpProfiledValue(char16 const * name, CallSiteInfo * callSiteInfo, uint count);
static void DumpProfiledValue(char16 const * name, ArrayCallSiteInfo * arrayCallSiteInfo, uint count);
static void DumpProfiledValue(char16 const * name, ImplicitCallFlags * loopImplicitCallFlags, uint count);
template<class TData, class FGetValueType>
static void DumpProfiledValuesGroupedByValue(const char16 *const name, const TData *const data, const uint count, const FGetValueType GetValueType, ArenaAllocator *const dynamicProfileInfoAllocator);
static void DumpFldInfoFlags(char16 const * name, FldInfo * fldInfo, uint count, FldInfoFlags value, char16 const * valueName);
static void DumpLoopInfo(FunctionBody *fbody);
#endif
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);
void ResetPolymorphicCallSiteInfo(ProfileId callSiteId, Js::LocalFunctionId functionId);
void SetFunctionIdSlotForNewPolymorphicCall(ProfileId callSiteId, Js::LocalFunctionId curFunctionId, Js::SourceId curSourceId, Js::FunctionBody *inliner);
void RecordPolymorphicCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId, FunctionInfo * calleeFunctionInfo);
#ifdef RUNTIME_DATA_COLLECTION
static CriticalSection s_csOutput;
template <typename T>
static void WriteData(const T& data, FILE * file);
#if defined(_MSC_VER) && !defined(__clang__)
template <>
static void WriteData<char16 const *>(char16 const * const& sz, FILE * file);
template <>
static void WriteData<FunctionInfo *>(FunctionInfo * const& functionInfo, FILE * file); // Not defined, to prevent accidentally writing function info
template <>
static void WriteData<FunctionBody *>(FunctionBody * const& functionInfo, FILE * file);
#endif
template <typename T>
static void WriteArray(uint count, T * arr, FILE * file);
template <typename T>
static void WriteArray(uint count, WriteBarrierPtr<T> arr, FILE * file);
#endif
#if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
Field(FunctionBody *) functionBody; // This will only be populated if NeedProfileInfoList is true
#endif
#ifdef DYNAMIC_PROFILE_STORAGE
// Used by de-serialize
DynamicProfileInfo();
template <typename T>
static DynamicProfileInfo * Deserialize(T * reader, Recycler* allocator, Js::LocalFunctionId * functionId);
template <typename T>
bool Serialize(T * writer);
static void UpdateSourceDynamicProfileManagers(ScriptContext * scriptContext);
#endif
static Js::LocalFunctionId const CallSiteMixed = (Js::LocalFunctionId)-1;
static Js::LocalFunctionId const CallSiteCrossContext = (Js::LocalFunctionId)-2;
static Js::LocalFunctionId const CallSiteNonFunction = (Js::LocalFunctionId)-3;
static Js::LocalFunctionId const CallSiteNoInfo = (Js::LocalFunctionId)-4;
static Js::LocalFunctionId const StartInvalidFunction = (Js::LocalFunctionId)-4;
static Js::SourceId const NoSourceId = (SourceId)-1;
static Js::SourceId const BuiltInSourceId = (SourceId)-2;
static Js::SourceId const CurrentSourceId = (SourceId)-3; // caller and callee in the same file
static Js::SourceId const InvalidSourceId = (SourceId)-4;
static Js::SourceId const JsBuiltInSourceId = (SourceId)-5;
bool MatchFunctionBody(FunctionBody * functionBody);
DynamicProfileInfo(FunctionBody * functionBody);
friend class SourceDynamicProfileManager;
public:
bool IsAggressiveIntTypeSpecDisabled(const bool isJitLoopBody) const
{
return
isJitLoopBody
? this->bits.disableAggressiveIntTypeSpec_jitLoopBody
: this->bits.disableAggressiveIntTypeSpec;
}
void DisableAggressiveIntTypeSpec(const bool isJitLoopBody)
{
this->bits.disableAggressiveIntTypeSpec_jitLoopBody = true;
if (!isJitLoopBody)
{
this->bits.disableAggressiveIntTypeSpec = true;
}
}
bool IsAggressiveMulIntTypeSpecDisabled(const bool isJitLoopBody) const
{
return
isJitLoopBody
? this->bits.disableAggressiveMulIntTypeSpec_jitLoopBody
: this->bits.disableAggressiveMulIntTypeSpec;
}
void DisableAggressiveMulIntTypeSpec(const bool isJitLoopBody)
{
this->bits.disableAggressiveMulIntTypeSpec_jitLoopBody = true;
if (!isJitLoopBody)
{
this->bits.disableAggressiveMulIntTypeSpec = true;
}
}
bool IsDivIntTypeSpecDisabled(const bool isJitLoopBody) const
{
return
isJitLoopBody
? this->bits.disableDivIntTypeSpec_jitLoopBody
: this->bits.disableDivIntTypeSpec;
}
void DisableDivIntTypeSpec(const bool isJitLoopBody)
{
this->bits.disableDivIntTypeSpec_jitLoopBody = true;
if (!isJitLoopBody)
{
this->bits.disableDivIntTypeSpec = true;
}
}
bool IsLossyIntTypeSpecDisabled() const { return bits.disableLossyIntTypeSpec; }
void DisableLossyIntTypeSpec() { this->bits.disableLossyIntTypeSpec = true; }
LoopFlags GetLoopFlags(int loopNumber) const
{
Assert(loopFlags);
return loopFlags->GetRange<LoopFlags>(loopNumber * LoopFlags::COUNT, LoopFlags::COUNT);
}
BVFixed * GetLoopFlags() const { return loopFlags; }
void SetLoopInterpreted(int loopNumber) { loopFlags->Set(loopNumber * LoopFlags::COUNT + LoopFlags::INTERPRETED); }
void SetMemOpMinReached(int loopNumber) { loopFlags->Set(loopNumber * LoopFlags::COUNT + LoopFlags::MEMOP_MIN_COUNT_FOUND); }
bool IsMemOpDisabled() const { return this->bits.disableMemOp; }
void DisableMemOp() { this->bits.disableMemOp = true; }
bool IsTrackCompoundedIntOverflowDisabled() const { return this->bits.disableTrackCompoundedIntOverflow; }
void DisableTrackCompoundedIntOverflow() { this->bits.disableTrackCompoundedIntOverflow = true; }
bool IsFloatTypeSpecDisabled() const { return this->bits.disableFloatTypeSpec; }
void DisableFloatTypeSpec() { this->bits.disableFloatTypeSpec = true; }
bool IsCheckThisDisabled() const { return this->bits.disableCheckThis; }
void DisableCheckThis() { this->bits.disableCheckThis = true; }
bool IsLoopImplicitCallInfoDisabled() const { return this->bits.disableLoopImplicitCallInfo; }
void DisableLoopImplicitCallInfo() { this->bits.disableLoopImplicitCallInfo = true; }
bool IsArrayCheckHoistDisabled(const bool isJitLoopBody) const
{
return
isJitLoopBody
? this->bits.disableArrayCheckHoist_jitLoopBody
: this->bits.disableArrayCheckHoist;
}
void DisableArrayCheckHoist(const bool isJitLoopBody)
{
this->bits.disableArrayCheckHoist_jitLoopBody = true;
if (!isJitLoopBody)
{
this->bits.disableArrayCheckHoist = true;
}
}
bool IsArrayMissingValueCheckHoistDisabled(const bool isJitLoopBody) const
{
return
isJitLoopBody
? this->bits.disableArrayMissingValueCheckHoist_jitLoopBody
: this->bits.disableArrayMissingValueCheckHoist;
}
void DisableArrayMissingValueCheckHoist(const bool isJitLoopBody)
{
this->bits.disableArrayMissingValueCheckHoist_jitLoopBody = true;
if (!isJitLoopBody)
{
this->bits.disableArrayMissingValueCheckHoist = true;
}
}
bool IsJsArraySegmentHoistDisabled(const bool isJitLoopBody) const
{
return
isJitLoopBody
? this->bits.disableJsArraySegmentHoist_jitLoopBody
: this->bits.disableJsArraySegmentHoist;
}
void DisableJsArraySegmentHoist(const bool isJitLoopBody)
{
this->bits.disableJsArraySegmentHoist_jitLoopBody = true;
if (!isJitLoopBody)
{
this->bits.disableJsArraySegmentHoist = true;
}
}
bool IsArrayLengthHoistDisabled(const bool isJitLoopBody) const
{
return
isJitLoopBody
? this->bits.disableArrayLengthHoist_jitLoopBody
: this->bits.disableArrayLengthHoist;
}
void DisableArrayLengthHoist(const bool isJitLoopBody)
{
this->bits.disableArrayLengthHoist_jitLoopBody = true;
if (!isJitLoopBody)
{
this->bits.disableArrayLengthHoist = true;
}
}
bool IsTypedArrayTypeSpecDisabled(const bool isJitLoopBody) const
{
return
isJitLoopBody
? this->bits.disableTypedArrayTypeSpec_jitLoopBody
: this->bits.disableTypedArrayTypeSpec;
}
void DisableTypedArrayTypeSpec(const bool isJitLoopBody)
{
this->bits.disableTypedArrayTypeSpec_jitLoopBody = true;
if (!isJitLoopBody)
{
this->bits.disableTypedArrayTypeSpec = true;
}
}
bool IsLdLenIntSpecDisabled() const { return this->bits.disableLdLenIntSpec; }
void DisableLdLenIntSpec() { this->bits.disableLdLenIntSpec = true; }
bool IsBoundCheckHoistDisabled(const bool isJitLoopBody) const
{
return
isJitLoopBody
? this->bits.disableBoundCheckHoist_jitLoopBody
: this->bits.disableBoundCheckHoist;
}
void DisableBoundCheckHoist(const bool isJitLoopBody)
{
this->bits.disableBoundCheckHoist_jitLoopBody = true;
if (!isJitLoopBody)
{
this->bits.disableBoundCheckHoist = true;
}
}
bool IsLoopCountBasedBoundCheckHoistDisabled(const bool isJitLoopBody) const
{
return
isJitLoopBody
? this->bits.disableLoopCountBasedBoundCheckHoist_jitLoopBody
: this->bits.disableLoopCountBasedBoundCheckHoist;
}
void DisableLoopCountBasedBoundCheckHoist(const bool isJitLoopBody)
{
this->bits.disableLoopCountBasedBoundCheckHoist_jitLoopBody = true;
if (!isJitLoopBody)
{
this->bits.disableLoopCountBasedBoundCheckHoist = true;
}
}
BYTE GetInlinerVersion() { return this->currentInlinerVersion; }
uint32 GetPolymorphicCacheState() const { return this->polymorphicCacheState; }
uint32 GetRecursiveInlineInfo() const { return this->m_recursiveInlineInfo; }
void SetHasNewPolyFieldAccess(FunctionBody *functionBody);
bool IsFloorInliningDisabled() const { return this->bits.disableFloorInlining; }
void DisableFloorInlining() { this->bits.disableFloorInlining = true; }
bool IsNoProfileBailoutsDisabled() const { return this->bits.disableNoProfileBailouts; }
void DisableNoProfileBailouts() { this->bits.disableNoProfileBailouts = true; }
bool IsSwitchOptDisabled() const { return this->bits.disableSwitchOpt; }
void DisableSwitchOpt() { this->bits.disableSwitchOpt = true; }
bool IsStackArgOptDisabled() const { return this->bits.disableStackArgOpt; }
void DisableStackArgOpt() { this->bits.disableStackArgOpt = true; }
bool IsEquivalentObjTypeSpecDisabled() const { return this->bits.disableEquivalentObjTypeSpec; }
void DisableEquivalentObjTypeSpec() { this->bits.disableEquivalentObjTypeSpec = true; }
bool IsObjTypeSpecDisabledInJitLoopBody() const { return this->bits.disableObjTypeSpec_jitLoopBody; }
void DisableObjTypeSpecInJitLoopBody() { this->bits.disableObjTypeSpec_jitLoopBody = true; }
bool IsPowIntIntTypeSpecDisabled() const { return bits.disablePowIntIntTypeSpec; }
void DisablePowIntIntTypeSpec() { this->bits.disablePowIntIntTypeSpec = true; }
bool IsTagCheckDisabled() const { return bits.disableTagCheck; }
void DisableTagCheck() { this->bits.disableTagCheck = true; }
bool IsOptimizeTryFinallyDisabled() const { return bits.disableOptimizeTryFinally; }
void DisableOptimizeTryFinally() { this->bits.disableOptimizeTryFinally = true; }
static bool IsCallSiteNoInfo(Js::LocalFunctionId functionId) { return functionId == CallSiteNoInfo; }
int IncRejitCount() { return this->rejitCount++; }
int GetRejitCount() { return this->rejitCount; }
void SetBailOutOffsetForLastRejit(uint32 offset) { this->bailOutOffsetForLastRejit = offset; }
uint32 GetBailOutOffsetForLastRejit() { return this->bailOutOffsetForLastRejit; }
#if DBG_DUMP
void Dump(FunctionBody* functionBody, ArenaAllocator * dynamicProfileInfoAllocator = nullptr);
#endif
private:
static void InstantiateForceInlinedMembers();
};
struct PolymorphicCallSiteInfo
{
Field(Js::LocalFunctionId) functionIds[DynamicProfileInfo::maxPolymorphicInliningSize];
Field(Js::SourceId) sourceIds[DynamicProfileInfo::maxPolymorphicInliningSize];
Field(PolymorphicCallSiteInfo *) next;
bool GetFunction(uint index, Js::LocalFunctionId *functionId, Js::SourceId *sourceId)
{
Assert(index < DynamicProfileInfo::maxPolymorphicInliningSize);
Assert(functionId);
Assert(sourceId);
if (DynamicProfileInfo::IsCallSiteNoInfo(functionIds[index]))
{
return false;
}
*functionId = functionIds[index];
*sourceId = sourceIds[index];
return true;
}
};
#ifdef DYNAMIC_PROFILE_STORAGE
class BufferReader
{
public:
BufferReader(__in_ecount(length) char const * buffer, size_t length) : current(buffer), lengthLeft(length) {}
template <typename T>
bool Read(T * data)
{
if (lengthLeft < sizeof(T))
{
return false;
}
*data = *(T *)current;
current += sizeof(T);
lengthLeft -= sizeof(T);
return true;
}
template <typename T>
bool Peek(T * data)
{
if (lengthLeft < sizeof(T))
{
return false;
}
*data = *(T *)current;
return true;
}
template <typename T>
bool ReadArray(__inout_ecount(len) T * data, size_t len)
{
size_t size = sizeof(T) * len;
if (lengthLeft < size)
{
return false;
}
memcpy_s(data, size, current, size);
current += size;
lengthLeft -= size;
return true;
}
private:
char const * current;
size_t lengthLeft;
};
class BufferSizeCounter
{
public:
BufferSizeCounter() : count(0) {}
size_t GetByteCount() const { return count; }
template <typename T>
bool Write(T const& data)
{
return WriteArray(&data, 1);
}
#if DBG_DUMP
void Log(DynamicProfileInfo* info) {}
#endif
template <typename T>
bool WriteArray(__in_ecount(len) T * data, size_t len)
{
count += sizeof(T) * len;
return true;
}
template <typename T>
bool WriteArray(WriteBarrierPtr<T> data, size_t len)
{
return WriteArray(static_cast<T*>(data), len);
}
private:
size_t count;
};
class BufferWriter
{
public:
BufferWriter(__in_ecount(length) char * buffer, size_t length) : current(buffer), lengthLeft(length) {}
template <typename T>
bool Write(T const& data)
{
return WriteArray(&data, 1);
}
#if DBG_DUMP
void Log(DynamicProfileInfo* info);
#endif
template <typename T>
bool WriteArray(__in_ecount(len) T * data, size_t len)
{
size_t size = sizeof(T) * len;
if (lengthLeft < size)
{
return false;
}
memcpy_s(current, size, data, size);
current += size;
lengthLeft -= size;
return true;
}
template <typename T>
bool WriteArray(WriteBarrierPtr<T> data, size_t len)
{
return WriteArray(static_cast<T*>(data), len);
}
private:
char * current;
size_t lengthLeft;
};
#endif
};
#endif