StructureStubInfo.h [plain text]
#pragma once
#include "CacheableIdentifier.h"
#include "CodeBlock.h"
#include "CodeOrigin.h"
#include "Instruction.h"
#include "JITStubRoutine.h"
#include "MacroAssembler.h"
#include "Options.h"
#include "RegisterSet.h"
#include "Structure.h"
#include "StructureSet.h"
#include "StructureStubClearingWatchpoint.h"
#include "StubInfoSummary.h"
#include <wtf/Box.h>
namespace JSC {
#if ENABLE(JIT)
class AccessCase;
class AccessGenerationResult;
class PolymorphicAccess;
enum class AccessType : int8_t {
GetById,
GetByIdWithThis,
GetByIdDirect,
TryGetById,
GetByVal,
Put,
In,
InstanceOf,
DeleteByID,
DeleteByVal,
GetPrivateName,
};
enum class CacheType : int8_t {
Unset,
GetByIdSelf,
PutByIdReplace,
InByIdSelf,
Stub,
ArrayLength,
StringLength
};
class StructureStubInfo {
WTF_MAKE_NONCOPYABLE(StructureStubInfo);
WTF_MAKE_FAST_ALLOCATED;
public:
StructureStubInfo(AccessType, CodeOrigin);
~StructureStubInfo();
void initGetByIdSelf(const ConcurrentJSLockerBase&, CodeBlock*, Structure* baseObjectStructure, PropertyOffset, CacheableIdentifier);
void initArrayLength(const ConcurrentJSLockerBase&);
void initStringLength(const ConcurrentJSLockerBase&);
void initPutByIdReplace(const ConcurrentJSLockerBase&, CodeBlock*, Structure* baseObjectStructure, PropertyOffset, CacheableIdentifier);
void initInByIdSelf(const ConcurrentJSLockerBase&, CodeBlock*, Structure* baseObjectStructure, PropertyOffset, CacheableIdentifier);
AccessGenerationResult addAccessCase(const GCSafeConcurrentJSLocker&, JSGlobalObject*, CodeBlock*, ECMAMode, CacheableIdentifier, std::unique_ptr<AccessCase>);
void reset(const ConcurrentJSLockerBase&, CodeBlock*);
void deref();
void aboutToDie();
void visitAggregate(SlotVisitor&);
void visitWeakReferences(const ConcurrentJSLockerBase&, CodeBlock*);
bool propagateTransitions(SlotVisitor&);
StubInfoSummary summary(VM&) const;
static StubInfoSummary summary(VM&, const StructureStubInfo*);
CacheableIdentifier identifier()
{
switch (m_cacheType) {
case CacheType::Unset:
case CacheType::ArrayLength:
case CacheType::StringLength:
case CacheType::Stub:
RELEASE_ASSERT_NOT_REACHED();
break;
case CacheType::PutByIdReplace:
case CacheType::InByIdSelf:
case CacheType::GetByIdSelf:
break;
}
return m_identifier;
}
bool containsPC(void* pc) const;
uint32_t inlineSize() const
{
int32_t inlineSize = MacroAssembler::differenceBetweenCodePtr(start, doneLocation);
ASSERT(inlineSize >= 0);
return inlineSize;
}
CodeLocationJump<JSInternalPtrTag> patchableJump()
{
ASSERT(accessType == AccessType::InstanceOf);
return start.jumpAtOffset<JSInternalPtrTag>(0);
}
JSValueRegs valueRegs() const
{
return JSValueRegs(
#if USE(JSVALUE32_64)
valueTagGPR,
#endif
valueGPR);
}
JSValueRegs propertyRegs() const
{
return JSValueRegs(
#if USE(JSVALUE32_64)
v.propertyTagGPR,
#endif
regs.propertyGPR);
}
JSValueRegs baseRegs() const
{
return JSValueRegs(
#if USE(JSVALUE32_64)
baseTagGPR,
#endif
baseGPR);
}
bool thisValueIsInThisGPR() const { return accessType == AccessType::GetByIdWithThis; }
#if ASSERT_ENABLED
void checkConsistency();
#else
ALWAYS_INLINE void checkConsistency() { }
#endif
CacheType cacheType() const { return m_cacheType; }
ALWAYS_INLINE bool considerCachingGeneric(VM& vm, CodeBlock* codeBlock, Structure* structure)
{
return considerCaching(vm, codeBlock, structure, CacheableIdentifier());
}
ALWAYS_INLINE bool considerCachingBy(VM& vm, CodeBlock* codeBlock, Structure* structure, CacheableIdentifier impl)
{
return considerCaching(vm, codeBlock, structure, impl);
}
private:
ALWAYS_INLINE bool considerCaching(VM& vm, CodeBlock* codeBlock, Structure* structure, CacheableIdentifier impl)
{
DisallowGC disallowGC;
if (!structure) {
sawNonCell = true;
return false;
}
everConsidered = true;
if (!countdown) {
WTF::incrementWithSaturation(repatchCount);
if (repatchCount > Options::repatchCountForCoolDown()) {
repatchCount = 0;
countdown = WTF::leftShiftWithSaturation(
static_cast<uint8_t>(Options::initialCoolDownCount()),
numberOfCoolDowns,
static_cast<uint8_t>(std::numeric_limits<uint8_t>::max() - 1));
WTF::incrementWithSaturation(numberOfCoolDowns);
bufferingCountdown = 0;
return true;
}
if (!bufferingCountdown) {
return true;
}
bufferingCountdown--;
bool isNewlyAdded = false;
{
auto locker = holdLock(m_bufferedStructuresLock);
isNewlyAdded = m_bufferedStructures.add({ structure, impl }).isNewEntry;
}
if (isNewlyAdded)
vm.heap.writeBarrier(codeBlock);
return isNewlyAdded;
}
countdown--;
return false;
}
void setCacheType(const ConcurrentJSLockerBase&, CacheType);
void clearBufferedStructures()
{
auto locker = holdLock(m_bufferedStructuresLock);
m_bufferedStructures.clear();
}
class BufferedStructure {
public:
static constexpr uintptr_t hashTableDeletedValue = 0x2;
BufferedStructure() = default;
BufferedStructure(Structure* structure, CacheableIdentifier byValId)
: m_structure(structure)
, m_byValId(byValId)
{ }
BufferedStructure(WTF::HashTableDeletedValueType)
: m_structure(bitwise_cast<Structure*>(hashTableDeletedValue))
{ }
bool isHashTableDeletedValue() const { return bitwise_cast<uintptr_t>(m_structure) == hashTableDeletedValue; }
unsigned hash() const
{
unsigned hash = PtrHash<Structure*>::hash(m_structure);
if (m_byValId)
hash += m_byValId.hash();
return hash;
}
friend bool operator==(const BufferedStructure& a, const BufferedStructure& b)
{
return a.m_structure == b.m_structure && a.m_byValId == b.m_byValId;
}
friend bool operator!=(const BufferedStructure& a, const BufferedStructure& b)
{
return !(a == b);
}
struct Hash {
static unsigned hash(const BufferedStructure& key)
{
return key.hash();
}
static bool equal(const BufferedStructure& a, const BufferedStructure& b)
{
return a == b;
}
static constexpr bool safeToCompareToEmptyOrDeleted = false;
};
using KeyTraits = SimpleClassHashTraits<BufferedStructure>;
static_assert(KeyTraits::emptyValueIsZero, "Structure* and CacheableIdentifier are empty if they are zero-initialized");
Structure* structure() const { return m_structure; }
const CacheableIdentifier& byValId() const { return m_byValId; }
private:
Structure* m_structure { nullptr };
CacheableIdentifier m_byValId;
};
public:
CodeOrigin codeOrigin;
union {
struct {
WriteBarrierBase<Structure> baseObjectStructure;
PropertyOffset offset;
} byIdSelf;
PolymorphicAccess* stub;
} u;
private:
CacheableIdentifier m_identifier;
HashSet<BufferedStructure, BufferedStructure::Hash, BufferedStructure::KeyTraits> m_bufferedStructures;
public:
CodeLocationLabel<JITStubRoutinePtrTag> start; CodeLocationLabel<JSInternalPtrTag> doneLocation;
CodeLocationCall<JSInternalPtrTag> slowPathCallLocation;
CodeLocationLabel<JITStubRoutinePtrTag> slowPathStartLocation;
RegisterSet usedRegisters;
GPRReg baseGPR;
GPRReg valueGPR;
union {
GPRReg thisGPR;
GPRReg prototypeGPR;
GPRReg propertyGPR;
} regs;
#if USE(JSVALUE32_64)
GPRReg valueTagGPR;
GPRReg baseTagGPR;
union {
GPRReg thisTagGPR;
GPRReg propertyTagGPR;
} v;
#endif
AccessType accessType;
private:
CacheType m_cacheType { CacheType::Unset };
public:
uint8_t countdown { 1 };
uint8_t repatchCount { 0 };
uint8_t numberOfCoolDowns { 0 };
CallSiteIndex callSiteIndex;
uint8_t bufferingCountdown;
bool resetByGC : 1;
bool tookSlowPath : 1;
bool everConsidered : 1;
bool prototypeIsKnownObject : 1; bool sawNonCell : 1;
bool hasConstantIdentifier : 1;
bool propertyIsString : 1;
bool propertyIsInt32 : 1;
bool propertyIsSymbol : 1;
private:
Lock m_bufferedStructuresLock;
};
inline CodeOrigin getStructureStubInfoCodeOrigin(StructureStubInfo& structureStubInfo)
{
return structureStubInfo.codeOrigin;
}
inline auto appropriateOptimizingGetByIdFunction(AccessType type) -> decltype(&operationGetByIdOptimize)
{
switch (type) {
case AccessType::GetById:
return operationGetByIdOptimize;
case AccessType::TryGetById:
return operationTryGetByIdOptimize;
case AccessType::GetByIdDirect:
return operationGetByIdDirectOptimize;
case AccessType::GetPrivateName:
return operationGetPrivateNameByIdOptimize;
case AccessType::GetByIdWithThis:
default:
ASSERT_NOT_REACHED();
return nullptr;
}
}
inline auto appropriateGenericGetByIdFunction(AccessType type) -> decltype(&operationGetByIdGeneric)
{
switch (type) {
case AccessType::GetById:
return operationGetByIdGeneric;
case AccessType::TryGetById:
return operationTryGetByIdGeneric;
case AccessType::GetByIdDirect:
return operationGetByIdDirectGeneric;
case AccessType::GetPrivateName:
return operationGetPrivateNameByIdGeneric;
case AccessType::GetByIdWithThis:
default:
ASSERT_NOT_REACHED();
return nullptr;
}
}
#else
class StructureStubInfo;
#endif // ENABLE(JIT)
typedef HashMap<CodeOrigin, StructureStubInfo*, CodeOriginApproximateHash> StubInfoMap;
}