StructureStubInfo.cpp [plain text]
#include "config.h"
#include "StructureStubInfo.h"
#include "JSObject.h"
#include "JSCInlines.h"
#include "PolymorphicAccess.h"
#include "Repatch.h"
namespace JSC {
#if ENABLE(JIT)
static const bool verbose = false;
StructureStubInfo::StructureStubInfo(AccessType accessType)
: callSiteIndex(UINT_MAX)
, accessType(accessType)
, cacheType(CacheType::Unset)
, countdown(1) , repatchCount(0)
, numberOfCoolDowns(0)
, bufferingCountdown(Options::repatchBufferingCountdown())
, resetByGC(false)
, tookSlowPath(false)
, everConsidered(false)
{
}
StructureStubInfo::~StructureStubInfo()
{
}
void StructureStubInfo::initGetByIdSelf(CodeBlock* codeBlock, Structure* baseObjectStructure, PropertyOffset offset)
{
cacheType = CacheType::GetByIdSelf;
u.byIdSelf.baseObjectStructure.set(
*codeBlock->vm(), codeBlock, baseObjectStructure);
u.byIdSelf.offset = offset;
}
void StructureStubInfo::initArrayLength()
{
cacheType = CacheType::ArrayLength;
}
void StructureStubInfo::initPutByIdReplace(CodeBlock* codeBlock, Structure* baseObjectStructure, PropertyOffset offset)
{
cacheType = CacheType::PutByIdReplace;
u.byIdSelf.baseObjectStructure.set(
*codeBlock->vm(), codeBlock, baseObjectStructure);
u.byIdSelf.offset = offset;
}
void StructureStubInfo::deref()
{
switch (cacheType) {
case CacheType::Stub:
delete u.stub;
return;
case CacheType::Unset:
case CacheType::GetByIdSelf:
case CacheType::PutByIdReplace:
case CacheType::ArrayLength:
return;
}
RELEASE_ASSERT_NOT_REACHED();
}
void StructureStubInfo::aboutToDie()
{
switch (cacheType) {
case CacheType::Stub:
u.stub->aboutToDie();
return;
case CacheType::Unset:
case CacheType::GetByIdSelf:
case CacheType::PutByIdReplace:
case CacheType::ArrayLength:
return;
}
RELEASE_ASSERT_NOT_REACHED();
}
AccessGenerationResult StructureStubInfo::addAccessCase(
const GCSafeConcurrentJSLocker& locker, CodeBlock* codeBlock, const Identifier& ident, std::unique_ptr<AccessCase> accessCase)
{
VM& vm = *codeBlock->vm();
if (verbose)
dataLog("Adding access case: ", accessCase, "\n");
if (!accessCase)
return AccessGenerationResult::GaveUp;
AccessGenerationResult result;
if (cacheType == CacheType::Stub) {
result = u.stub->addCase(locker, vm, codeBlock, *this, ident, WTFMove(accessCase));
if (verbose)
dataLog("Had stub, result: ", result, "\n");
if (!result.buffered()) {
bufferedStructures.clear();
return result;
}
} else {
std::unique_ptr<PolymorphicAccess> access = std::make_unique<PolymorphicAccess>();
Vector<std::unique_ptr<AccessCase>, 2> accessCases;
std::unique_ptr<AccessCase> previousCase =
AccessCase::fromStructureStubInfo(vm, codeBlock, *this);
if (previousCase)
accessCases.append(WTFMove(previousCase));
accessCases.append(WTFMove(accessCase));
result = access->addCases(locker, vm, codeBlock, *this, ident, WTFMove(accessCases));
if (verbose)
dataLog("Created stub, result: ", result, "\n");
if (!result.buffered()) {
bufferedStructures.clear();
return result;
}
cacheType = CacheType::Stub;
u.stub = access.release();
}
RELEASE_ASSERT(!result.generatedSomeCode());
if (!result.buffered()) {
if (verbose)
dataLog("Didn't buffer anything, bailing.\n");
bufferedStructures.clear();
return result;
}
if (bufferingCountdown) {
if (verbose)
dataLog("Countdown is too high: ", bufferingCountdown, ".\n");
return result;
}
bufferedStructures.clear();
result = u.stub->regenerate(locker, vm, codeBlock, *this, ident);
if (verbose)
dataLog("Regeneration result: ", result, "\n");
RELEASE_ASSERT(!result.buffered());
if (!result.generatedSomeCode())
return result;
bufferingCountdown = Options::repatchBufferingCountdown();
return result;
}
void StructureStubInfo::reset(CodeBlock* codeBlock)
{
bufferedStructures.clear();
if (cacheType == CacheType::Unset)
return;
if (Options::verboseOSR()) {
dataLog("Clearing structure cache (kind ", static_cast<int>(accessType), ") in ", RawPointer(codeBlock), ".\n");
}
switch (accessType) {
case AccessType::TryGet:
resetGetByID(codeBlock, *this, GetByIDKind::Try);
break;
case AccessType::Get:
resetGetByID(codeBlock, *this, GetByIDKind::Normal);
break;
case AccessType::GetWithThis:
resetGetByID(codeBlock, *this, GetByIDKind::WithThis);
break;
case AccessType::Put:
resetPutByID(codeBlock, *this);
break;
case AccessType::In:
resetIn(codeBlock, *this);
break;
}
deref();
cacheType = CacheType::Unset;
}
void StructureStubInfo::visitWeakReferences(CodeBlock* codeBlock)
{
VM& vm = *codeBlock->vm();
bufferedStructures.genericFilter(
[&] (Structure* structure) -> bool {
return Heap::isMarked(structure);
});
switch (cacheType) {
case CacheType::GetByIdSelf:
case CacheType::PutByIdReplace:
if (Heap::isMarked(u.byIdSelf.baseObjectStructure.get()))
return;
break;
case CacheType::Stub:
if (u.stub->visitWeak(vm))
return;
break;
default:
return;
}
reset(codeBlock);
resetByGC = true;
}
bool StructureStubInfo::propagateTransitions(SlotVisitor& visitor)
{
switch (cacheType) {
case CacheType::Unset:
case CacheType::ArrayLength:
return true;
case CacheType::GetByIdSelf:
case CacheType::PutByIdReplace:
return u.byIdSelf.baseObjectStructure->markIfCheap(visitor);
case CacheType::Stub:
return u.stub->propagateTransitions(visitor);
}
RELEASE_ASSERT_NOT_REACHED();
return true;
}
bool StructureStubInfo::containsPC(void* pc) const
{
if (cacheType != CacheType::Stub)
return false;
return u.stub->containsPC(pc);
}
#endif // ENABLE(JIT)
}