#include "config.h"
#include "AccessCase.h"
#if ENABLE(JIT)
#include "CCallHelpers.h"
#include "CallLinkInfo.h"
#include "DOMJITGetterSetter.h"
#include "DirectArguments.h"
#include "GetterSetter.h"
#include "GetterSetterAccessCase.h"
#include "HeapInlines.h"
#include "IntrinsicGetterAccessCase.h"
#include "JSCJSValueInlines.h"
#include "JSModuleEnvironment.h"
#include "JSModuleNamespaceObject.h"
#include "LinkBuffer.h"
#include "ModuleNamespaceAccessCase.h"
#include "PolymorphicAccess.h"
#include "ScopedArguments.h"
#include "ScratchRegisterAllocator.h"
#include "SlotVisitorInlines.h"
#include "StructureStubInfo.h"
#include "ThunkGenerators.h"
namespace JSC {
static const bool verbose = false;
AccessCase::AccessCase(VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet)
: m_type(type)
, m_offset(offset)
{
m_structure.setMayBeNull(vm, owner, structure);
m_conditionSet = conditionSet;
}
std::unique_ptr<AccessCase> AccessCase::create(VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet)
{
switch (type) {
case InHit:
case InMiss:
case ArrayLength:
case StringLength:
case DirectArgumentsLength:
case ScopedArgumentsLength:
case ModuleNamespaceLoad:
case Replace:
break;
default:
ASSERT_NOT_REACHED();
};
return std::unique_ptr<AccessCase>(new AccessCase(vm, owner, type, offset, structure, conditionSet));
}
std::unique_ptr<AccessCase> AccessCase::create(
VM& vm, JSCell* owner, PropertyOffset offset, Structure* oldStructure, Structure* newStructure,
const ObjectPropertyConditionSet& conditionSet)
{
RELEASE_ASSERT(oldStructure == newStructure->previousID());
if (GPRInfo::numberOfRegisters < 6
&& oldStructure->outOfLineCapacity() != newStructure->outOfLineCapacity()
&& oldStructure->outOfLineCapacity()) {
return nullptr;
}
return std::unique_ptr<AccessCase>(new AccessCase(vm, owner, Transition, offset, newStructure, conditionSet));
}
AccessCase::~AccessCase()
{
}
std::unique_ptr<AccessCase> AccessCase::fromStructureStubInfo(
VM& vm, JSCell* owner, StructureStubInfo& stubInfo)
{
switch (stubInfo.cacheType) {
case CacheType::GetByIdSelf:
return ProxyableAccessCase::create(vm, owner, Load, stubInfo.u.byIdSelf.offset, stubInfo.u.byIdSelf.baseObjectStructure.get());
case CacheType::PutByIdReplace:
return AccessCase::create(vm, owner, Replace, stubInfo.u.byIdSelf.offset, stubInfo.u.byIdSelf.baseObjectStructure.get());
default:
return nullptr;
}
}
std::unique_ptr<AccessCase> AccessCase::clone() const
{
std::unique_ptr<AccessCase> result(new AccessCase(*this));
result->resetState();
return result;
}
Vector<WatchpointSet*, 2> AccessCase::commit(VM& vm, const Identifier& ident)
{
RELEASE_ASSERT(m_state == Primordial || m_state == Committed);
Vector<WatchpointSet*, 2> result;
if ((structure() && structure()->needImpurePropertyWatchpoint())
|| m_conditionSet.needImpurePropertyWatchpoint())
result.append(vm.ensureWatchpointSetForImpureProperty(ident));
if (additionalSet())
result.append(additionalSet());
m_state = Committed;
return result;
}
bool AccessCase::guardedByStructureCheck() const
{
if (viaProxy())
return false;
switch (m_type) {
case ArrayLength:
case StringLength:
case DirectArgumentsLength:
case ScopedArgumentsLength:
case ModuleNamespaceLoad:
return false;
default:
return true;
}
}
bool AccessCase::doesCalls(Vector<JSCell*>* cellsToMark) const
{
switch (type()) {
case Getter:
case Setter:
case CustomValueGetter:
case CustomAccessorGetter:
case CustomValueSetter:
case CustomAccessorSetter:
return true;
case Transition:
if (newStructure()->outOfLineCapacity() != structure()->outOfLineCapacity()
&& structure()->couldHaveIndexingHeader()) {
if (cellsToMark)
cellsToMark->append(newStructure());
return true;
}
return false;
default:
return false;
}
}
bool AccessCase::couldStillSucceed() const
{
return m_conditionSet.structuresEnsureValidityAssumingImpurePropertyWatchpoint();
}
bool AccessCase::canReplace(const AccessCase& other) const
{
switch (type()) {
case ArrayLength:
case StringLength:
case DirectArgumentsLength:
case ScopedArgumentsLength:
return other.type() == type();
case ModuleNamespaceLoad: {
if (other.type() != type())
return false;
auto& thisCase = this->as<ModuleNamespaceAccessCase>();
auto& otherCase = this->as<ModuleNamespaceAccessCase>();
return thisCase.moduleNamespaceObject() == otherCase.moduleNamespaceObject();
}
default:
if (!guardedByStructureCheck() || !other.guardedByStructureCheck())
return false;
return structure() == other.structure();
}
}
void AccessCase::dump(PrintStream& out) const
{
out.print(m_type, ":(");
CommaPrinter comma;
out.print(comma, m_state);
if (m_type == Transition)
out.print(comma, "structure = ", pointerDump(structure()), " -> ", pointerDump(newStructure()));
else if (m_structure)
out.print(comma, "structure = ", pointerDump(m_structure.get()));
if (isValidOffset(m_offset))
out.print(comma, "offset = ", m_offset);
if (!m_conditionSet.isEmpty())
out.print(comma, "conditions = ", m_conditionSet);
dumpImpl(out, comma);
out.print(")");
}
bool AccessCase::visitWeak(VM& vm) const
{
if (m_structure && !Heap::isMarked(m_structure.get()))
return false;
if (!m_conditionSet.areStillLive())
return false;
if (isAccessor()) {
auto& accessor = this->as<GetterSetterAccessCase>();
if (accessor.callLinkInfo())
accessor.callLinkInfo()->visitWeak(vm);
if (accessor.customSlotBase() && !Heap::isMarked(accessor.customSlotBase()))
return false;
} else if (type() == IntrinsicGetter) {
auto& intrinsic = this->as<IntrinsicGetterAccessCase>();
if (intrinsic.intrinsicFunction() && !Heap::isMarked(intrinsic.intrinsicFunction()))
return false;
} else if (type() == ModuleNamespaceLoad) {
auto& accessCase = this->as<ModuleNamespaceAccessCase>();
if (accessCase.moduleNamespaceObject() && !Heap::isMarked(accessCase.moduleNamespaceObject()))
return false;
if (accessCase.moduleEnvironment() && !Heap::isMarked(accessCase.moduleEnvironment()))
return false;
}
return true;
}
bool AccessCase::propagateTransitions(SlotVisitor& visitor) const
{
bool result = true;
if (m_structure)
result &= m_structure->markIfCheap(visitor);
switch (m_type) {
case Transition:
if (Heap::isMarkedConcurrently(m_structure->previousID()))
visitor.appendUnbarriered(m_structure.get());
else
result = false;
break;
default:
break;
}
return result;
}
void AccessCase::generateWithGuard(
AccessGenerationState& state, CCallHelpers::JumpList& fallThrough)
{
SuperSamplerScope superSamplerScope(false);
RELEASE_ASSERT(m_state == Committed);
m_state = Generated;
CCallHelpers& jit = *state.jit;
VM& vm = state.m_vm;
JSValueRegs valueRegs = state.valueRegs;
GPRReg baseGPR = state.baseGPR;
GPRReg scratchGPR = state.scratchGPR;
UNUSED_PARAM(vm);
switch (m_type) {
case ArrayLength: {
ASSERT(!viaProxy());
jit.load8(CCallHelpers::Address(baseGPR, JSCell::indexingTypeAndMiscOffset()), scratchGPR);
fallThrough.append(
jit.branchTest32(
CCallHelpers::Zero, scratchGPR, CCallHelpers::TrustedImm32(IsArray)));
fallThrough.append(
jit.branchTest32(
CCallHelpers::Zero, scratchGPR, CCallHelpers::TrustedImm32(IndexingShapeMask)));
break;
}
case StringLength: {
ASSERT(!viaProxy());
fallThrough.append(
jit.branch8(
CCallHelpers::NotEqual,
CCallHelpers::Address(baseGPR, JSCell::typeInfoTypeOffset()),
CCallHelpers::TrustedImm32(StringType)));
break;
}
case DirectArgumentsLength: {
ASSERT(!viaProxy());
fallThrough.append(
jit.branch8(
CCallHelpers::NotEqual,
CCallHelpers::Address(baseGPR, JSCell::typeInfoTypeOffset()),
CCallHelpers::TrustedImm32(DirectArgumentsType)));
fallThrough.append(
jit.branchTestPtr(
CCallHelpers::NonZero,
CCallHelpers::Address(baseGPR, DirectArguments::offsetOfMappedArguments())));
jit.load32(
CCallHelpers::Address(baseGPR, DirectArguments::offsetOfLength()),
valueRegs.payloadGPR());
jit.boxInt32(valueRegs.payloadGPR(), valueRegs);
state.succeed();
return;
}
case ScopedArgumentsLength: {
ASSERT(!viaProxy());
fallThrough.append(
jit.branch8(
CCallHelpers::NotEqual,
CCallHelpers::Address(baseGPR, JSCell::typeInfoTypeOffset()),
CCallHelpers::TrustedImm32(ScopedArgumentsType)));
fallThrough.append(
jit.branchTest8(
CCallHelpers::NonZero,
CCallHelpers::Address(baseGPR, ScopedArguments::offsetOfOverrodeThings())));
jit.load32(
CCallHelpers::Address(baseGPR, ScopedArguments::offsetOfTotalLength()),
valueRegs.payloadGPR());
jit.boxInt32(valueRegs.payloadGPR(), valueRegs);
state.succeed();
return;
}
case ModuleNamespaceLoad: {
this->as<ModuleNamespaceAccessCase>().emit(state, fallThrough);
return;
}
default: {
if (viaProxy()) {
fallThrough.append(
jit.branch8(
CCallHelpers::NotEqual,
CCallHelpers::Address(baseGPR, JSCell::typeInfoTypeOffset()),
CCallHelpers::TrustedImm32(PureForwardingProxyType)));
jit.loadPtr(CCallHelpers::Address(baseGPR, JSProxy::targetOffset()), scratchGPR);
fallThrough.append(
jit.branchStructure(
CCallHelpers::NotEqual,
CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()),
structure()));
} else {
fallThrough.append(
jit.branchStructure(
CCallHelpers::NotEqual,
CCallHelpers::Address(baseGPR, JSCell::structureIDOffset()),
structure()));
}
break;
} };
generateImpl(state);
}
void AccessCase::generate(AccessGenerationState& state)
{
RELEASE_ASSERT(m_state == Committed);
m_state = Generated;
generateImpl(state);
}
void AccessCase::generateImpl(AccessGenerationState& state)
{
SuperSamplerScope superSamplerScope(false);
if (verbose)
dataLog("\n\nGenerating code for: ", *this, "\n");
ASSERT(m_state == Generated);
CCallHelpers& jit = *state.jit;
VM& vm = state.m_vm;
CodeBlock* codeBlock = jit.codeBlock();
StructureStubInfo& stubInfo = *state.stubInfo;
const Identifier& ident = *state.ident;
JSValueRegs valueRegs = state.valueRegs;
GPRReg baseGPR = state.baseGPR;
GPRReg thisGPR = state.thisGPR != InvalidGPRReg ? state.thisGPR : baseGPR;
GPRReg scratchGPR = state.scratchGPR;
ASSERT(m_conditionSet.structuresEnsureValidityAssumingImpurePropertyWatchpoint());
for (const ObjectPropertyCondition& condition : m_conditionSet) {
Structure* structure = condition.object()->structure();
if (condition.isWatchableAssumingImpurePropertyWatchpoint()) {
structure->addTransitionWatchpoint(state.addWatchpoint(condition));
continue;
}
if (!condition.structureEnsuresValidityAssumingImpurePropertyWatchpoint(structure)) {
dataLog("This condition is no longer met: ", condition, "\n");
RELEASE_ASSERT_NOT_REACHED();
}
state.weakReferences.append(WriteBarrier<JSCell>(vm, codeBlock, structure));
jit.move(CCallHelpers::TrustedImmPtr(condition.object()), scratchGPR);
state.failAndRepatch.append(
jit.branchStructure(
CCallHelpers::NotEqual,
CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()),
structure));
}
switch (m_type) {
case InHit:
case InMiss:
jit.boxBooleanPayload(m_type == InHit, valueRegs.payloadGPR());
state.succeed();
return;
case Miss:
jit.moveTrustedValue(jsUndefined(), valueRegs);
state.succeed();
return;
case Load:
case GetGetter:
case Getter:
case Setter:
case CustomValueGetter:
case CustomAccessorGetter:
case CustomValueSetter:
case CustomAccessorSetter: {
GPRReg valueRegsPayloadGPR = valueRegs.payloadGPR();
if (isValidOffset(m_offset)) {
Structure* currStructure;
if (m_conditionSet.isEmpty())
currStructure = structure();
else
currStructure = m_conditionSet.slotBaseCondition().object()->structure();
currStructure->startWatchingPropertyForReplacements(vm, offset());
}
GPRReg baseForGetGPR;
if (viaProxy()) {
ASSERT(m_type != CustomValueSetter || m_type != CustomAccessorSetter); if (m_type == Getter || m_type == Setter)
baseForGetGPR = scratchGPR;
else
baseForGetGPR = valueRegsPayloadGPR;
ASSERT((m_type != Getter && m_type != Setter) || baseForGetGPR != baseGPR);
ASSERT(m_type != Setter || baseForGetGPR != valueRegsPayloadGPR);
jit.loadPtr(
CCallHelpers::Address(baseGPR, JSProxy::targetOffset()),
baseForGetGPR);
} else
baseForGetGPR = baseGPR;
GPRReg baseForAccessGPR;
if (!m_conditionSet.isEmpty()) {
jit.move(
CCallHelpers::TrustedImmPtr(alternateBase()),
scratchGPR);
baseForAccessGPR = scratchGPR;
} else
baseForAccessGPR = baseForGetGPR;
GPRReg loadedValueGPR = InvalidGPRReg;
if (m_type != CustomValueGetter && m_type != CustomAccessorGetter && m_type != CustomValueSetter && m_type != CustomAccessorSetter) {
if (m_type == Load || m_type == GetGetter)
loadedValueGPR = valueRegsPayloadGPR;
else
loadedValueGPR = scratchGPR;
ASSERT((m_type != Getter && m_type != Setter) || loadedValueGPR != baseGPR);
ASSERT(m_type != Setter || loadedValueGPR != valueRegsPayloadGPR);
GPRReg storageGPR;
if (isInlineOffset(m_offset))
storageGPR = baseForAccessGPR;
else {
jit.loadPtr(
CCallHelpers::Address(baseForAccessGPR, JSObject::butterflyOffset()),
loadedValueGPR);
storageGPR = loadedValueGPR;
}
#if USE(JSVALUE64)
jit.load64(
CCallHelpers::Address(storageGPR, offsetRelativeToBase(m_offset)), loadedValueGPR);
#else
if (m_type == Load || m_type == GetGetter) {
jit.load32(
CCallHelpers::Address(storageGPR, offsetRelativeToBase(m_offset) + TagOffset),
valueRegs.tagGPR());
}
jit.load32(
CCallHelpers::Address(storageGPR, offsetRelativeToBase(m_offset) + PayloadOffset),
loadedValueGPR);
#endif
}
if (m_type == Load || m_type == GetGetter) {
state.succeed();
return;
}
if (Options::useDOMJIT() && m_type == CustomAccessorGetter && this->as<GetterSetterAccessCase>().domJIT()) {
auto& access = this->as<GetterSetterAccessCase>();
if (structure()->classInfo()->isSubClassOf(access.domJIT()->thisClassInfo())) {
access.emitDOMJITGetter(state, baseForGetGPR);
return;
}
}
CCallHelpers::Call operationCall;
CCallHelpers::DataLabelPtr addressOfLinkFunctionCheck;
CCallHelpers::Call fastPathCall;
CCallHelpers::Call slowPathCall;
AccessGenerationState::SpillState spillState = state.preserveLiveRegistersToStackForCall();
auto restoreLiveRegistersFromStackForCall = [&](AccessGenerationState::SpillState& spillState, bool callHasReturnValue) {
RegisterSet dontRestore;
if (callHasReturnValue) {
dontRestore.set(valueRegs);
}
state.restoreLiveRegistersFromStackForCall(spillState, dontRestore);
};
jit.store32(
CCallHelpers::TrustedImm32(state.callSiteIndexForExceptionHandlingOrOriginal().bits()),
CCallHelpers::tagFor(static_cast<VirtualRegister>(CallFrameSlot::argumentCount)));
if (m_type == Getter || m_type == Setter) {
auto& access = this->as<GetterSetterAccessCase>();
ASSERT(baseGPR != loadedValueGPR);
ASSERT(m_type != Setter || (baseGPR != valueRegsPayloadGPR && loadedValueGPR != valueRegsPayloadGPR));
state.setSpillStateForJSGetterSetter(spillState);
RELEASE_ASSERT(!access.callLinkInfo());
access.m_callLinkInfo = std::make_unique<CallLinkInfo>();
access.callLinkInfo()->disallowStubs();
access.callLinkInfo()->setUpCall(
CallLinkInfo::Call, stubInfo.codeOrigin, loadedValueGPR);
CCallHelpers::JumpList done;
unsigned numberOfParameters = 1;
if (m_type == Setter)
numberOfParameters++;
if (m_type == Setter) {
jit.loadPtr(
CCallHelpers::Address(loadedValueGPR, GetterSetter::offsetOfSetter()),
loadedValueGPR);
} else {
jit.loadPtr(
CCallHelpers::Address(loadedValueGPR, GetterSetter::offsetOfGetter()),
loadedValueGPR);
}
CCallHelpers::Jump returnUndefined = jit.branchTestPtr(
CCallHelpers::Zero, loadedValueGPR);
unsigned numberOfRegsForCall = CallFrame::headerSizeInRegisters + numberOfParameters;
unsigned numberOfBytesForCall = numberOfRegsForCall * sizeof(Register) - sizeof(CallerFrameAndPC);
unsigned alignedNumberOfBytesForCall =
WTF::roundUpToMultipleOf(stackAlignmentBytes(), numberOfBytesForCall);
jit.subPtr(
CCallHelpers::TrustedImm32(alignedNumberOfBytesForCall),
CCallHelpers::stackPointerRegister);
CCallHelpers::Address calleeFrame = CCallHelpers::Address(
CCallHelpers::stackPointerRegister,
-static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC)));
jit.store32(
CCallHelpers::TrustedImm32(numberOfParameters),
calleeFrame.withOffset(CallFrameSlot::argumentCount * sizeof(Register) + PayloadOffset));
jit.storeCell(
loadedValueGPR, calleeFrame.withOffset(CallFrameSlot::callee * sizeof(Register)));
jit.storeCell(
thisGPR,
calleeFrame.withOffset(virtualRegisterForArgument(0).offset() * sizeof(Register)));
if (m_type == Setter) {
jit.storeValue(
valueRegs,
calleeFrame.withOffset(
virtualRegisterForArgument(1).offset() * sizeof(Register)));
}
CCallHelpers::Jump slowCase = jit.branchPtrWithPatch(
CCallHelpers::NotEqual, loadedValueGPR, addressOfLinkFunctionCheck,
CCallHelpers::TrustedImmPtr(0));
fastPathCall = jit.nearCall();
if (m_type == Getter)
jit.setupResults(valueRegs);
done.append(jit.jump());
slowCase.link(&jit);
jit.move(loadedValueGPR, GPRInfo::regT0);
#if USE(JSVALUE32_64)
jit.move(CCallHelpers::TrustedImm32(JSValue::CellTag), GPRInfo::regT1);
#endif
jit.move(CCallHelpers::TrustedImmPtr(access.callLinkInfo()), GPRInfo::regT2);
slowPathCall = jit.nearCall();
if (m_type == Getter)
jit.setupResults(valueRegs);
done.append(jit.jump());
returnUndefined.link(&jit);
if (m_type == Getter)
jit.moveTrustedValue(jsUndefined(), valueRegs);
done.link(&jit);
jit.addPtr(CCallHelpers::TrustedImm32((codeBlock->stackPointerOffset() * sizeof(Register)) - state.preservedReusedRegisterState.numberOfBytesPreserved - spillState.numberOfStackBytesUsedForRegisterPreservation),
GPRInfo::callFrameRegister, CCallHelpers::stackPointerRegister);
bool callHasReturnValue = isGetter();
restoreLiveRegistersFromStackForCall(spillState, callHasReturnValue);
jit.addLinkTask([=, &vm] (LinkBuffer& linkBuffer) {
this->as<GetterSetterAccessCase>().callLinkInfo()->setCallLocations(
CodeLocationLabel(linkBuffer.locationOfNearCall(slowPathCall)),
CodeLocationLabel(linkBuffer.locationOf(addressOfLinkFunctionCheck)),
linkBuffer.locationOfNearCall(fastPathCall));
linkBuffer.link(
slowPathCall,
CodeLocationLabel(vm.getCTIStub(linkCallThunkGenerator).code()));
});
} else {
ASSERT(m_type == CustomValueGetter || m_type == CustomAccessorGetter || m_type == CustomValueSetter || m_type == CustomAccessorSetter);
jit.makeSpaceOnStackForCCall();
GPRReg baseForCustomGetGPR = baseGPR != thisGPR ? thisGPR : baseForGetGPR;
GPRReg baseForCustom = m_type == CustomValueGetter || m_type == CustomValueSetter ? baseForAccessGPR : baseForCustomGetGPR;
#if USE(JSVALUE64)
if (m_type == CustomValueGetter || m_type == CustomAccessorGetter) {
jit.setupArgumentsWithExecState(
baseForCustom,
CCallHelpers::TrustedImmPtr(ident.impl()));
} else
jit.setupArgumentsWithExecState(baseForCustom, valueRegs.gpr());
#else
if (m_type == CustomValueGetter || m_type == CustomAccessorGetter) {
jit.setupArgumentsWithExecState(
EABI_32BIT_DUMMY_ARG baseForCustom,
CCallHelpers::TrustedImm32(JSValue::CellTag),
CCallHelpers::TrustedImmPtr(ident.impl()));
} else {
jit.setupArgumentsWithExecState(
EABI_32BIT_DUMMY_ARG baseForCustom,
CCallHelpers::TrustedImm32(JSValue::CellTag),
valueRegs.payloadGPR(), valueRegs.tagGPR());
}
#endif
jit.storePtr(GPRInfo::callFrameRegister, &vm.topCallFrame);
operationCall = jit.call();
jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
linkBuffer.link(operationCall, FunctionPtr(this->as<GetterSetterAccessCase>().m_customAccessor.opaque));
});
if (m_type == CustomValueGetter || m_type == CustomAccessorGetter)
jit.setupResults(valueRegs);
jit.reclaimSpaceOnStackForCCall();
CCallHelpers::Jump noException =
jit.emitExceptionCheck(vm, CCallHelpers::InvertedExceptionCheck);
state.restoreLiveRegistersFromStackForCallWithThrownException(spillState);
state.emitExplicitExceptionHandler();
noException.link(&jit);
bool callHasReturnValue = isGetter();
restoreLiveRegistersFromStackForCall(spillState, callHasReturnValue);
}
state.succeed();
return;
}
case Replace: {
if (InferredType* type = structure()->inferredTypeFor(ident.impl())) {
if (verbose)
dataLog("Have type: ", type->descriptor(), "\n");
state.failAndRepatch.append(
jit.branchIfNotType(valueRegs, scratchGPR, type->descriptor()));
} else if (verbose)
dataLog("Don't have type.\n");
if (isInlineOffset(m_offset)) {
jit.storeValue(
valueRegs,
CCallHelpers::Address(
baseGPR,
JSObject::offsetOfInlineStorage() +
offsetInInlineStorage(m_offset) * sizeof(JSValue)));
} else {
jit.loadPtr(CCallHelpers::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR);
jit.storeValue(
valueRegs,
CCallHelpers::Address(
scratchGPR, offsetInButterfly(m_offset) * sizeof(JSValue)));
}
state.succeed();
return;
}
case Transition: {
RELEASE_ASSERT(GPRInfo::numberOfRegisters >= 6 || !structure()->outOfLineCapacity() || structure()->outOfLineCapacity() == newStructure()->outOfLineCapacity());
if (InferredType* type = newStructure()->inferredTypeFor(ident.impl())) {
if (verbose)
dataLog("Have type: ", type->descriptor(), "\n");
state.failAndRepatch.append(
jit.branchIfNotType(valueRegs, scratchGPR, type->descriptor()));
} else if (verbose)
dataLog("Don't have type.\n");
bool allocating = newStructure()->outOfLineCapacity() != structure()->outOfLineCapacity();
bool reallocating = allocating && structure()->outOfLineCapacity();
bool allocatingInline = allocating && !structure()->couldHaveIndexingHeader();
ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters);
allocator.lock(baseGPR);
#if USE(JSVALUE32_64)
allocator.lock(static_cast<GPRReg>(stubInfo.patch.baseTagGPR));
#endif
allocator.lock(valueRegs);
allocator.lock(scratchGPR);
GPRReg scratchGPR2 = InvalidGPRReg;
GPRReg scratchGPR3 = InvalidGPRReg;
if (allocatingInline) {
scratchGPR2 = allocator.allocateScratchGPR();
scratchGPR3 = allocator.allocateScratchGPR();
}
ScratchRegisterAllocator::PreservedState preservedState =
allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::SpaceForCCall);
CCallHelpers::JumpList slowPath;
ASSERT(structure()->transitionWatchpointSetHasBeenInvalidated());
if (allocating) {
size_t newSize = newStructure()->outOfLineCapacity() * sizeof(JSValue);
if (allocatingInline) {
MarkedAllocator* allocator = vm.auxiliarySpace.allocatorFor(newSize);
if (!allocator) {
slowPath.append(jit.jump());
}
jit.move(CCallHelpers::TrustedImmPtr(allocator), scratchGPR2);
jit.emitAllocate(scratchGPR, allocator, scratchGPR2, scratchGPR3, slowPath);
jit.addPtr(CCallHelpers::TrustedImm32(newSize + sizeof(IndexingHeader)), scratchGPR);
size_t oldSize = structure()->outOfLineCapacity() * sizeof(JSValue);
ASSERT(newSize > oldSize);
if (reallocating) {
jit.loadPtr(CCallHelpers::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR3);
for (size_t offset = 0; offset < oldSize; offset += sizeof(void*)) {
jit.loadPtr(
CCallHelpers::Address(
scratchGPR3,
-static_cast<ptrdiff_t>(
offset + sizeof(JSValue) + sizeof(void*))),
scratchGPR2);
jit.storePtr(
scratchGPR2,
CCallHelpers::Address(
scratchGPR,
-static_cast<ptrdiff_t>(offset + sizeof(JSValue) + sizeof(void*))));
}
}
for (size_t offset = oldSize; offset < newSize; offset += sizeof(void*))
jit.storePtr(CCallHelpers::TrustedImmPtr(0), CCallHelpers::Address(scratchGPR, -static_cast<ptrdiff_t>(offset + sizeof(JSValue) + sizeof(void*))));
} else {
RegisterSet extraRegistersToPreserve;
extraRegistersToPreserve.set(baseGPR);
extraRegistersToPreserve.set(valueRegs);
AccessGenerationState::SpillState spillState = state.preserveLiveRegistersToStackForCall(extraRegistersToPreserve);
jit.store32(
CCallHelpers::TrustedImm32(
state.callSiteIndexForExceptionHandlingOrOriginal().bits()),
CCallHelpers::tagFor(static_cast<VirtualRegister>(CallFrameSlot::argumentCount)));
jit.makeSpaceOnStackForCCall();
if (!reallocating) {
jit.setupArgumentsWithExecState(baseGPR);
CCallHelpers::Call operationCall = jit.call();
jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
linkBuffer.link(
operationCall,
FunctionPtr(operationReallocateButterflyToHavePropertyStorageWithInitialCapacity));
});
} else {
jit.setupArgumentsWithExecState(
baseGPR, CCallHelpers::TrustedImm32(newSize / sizeof(JSValue)));
CCallHelpers::Call operationCall = jit.call();
jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
linkBuffer.link(
operationCall,
FunctionPtr(operationReallocateButterflyToGrowPropertyStorage));
});
}
jit.reclaimSpaceOnStackForCCall();
jit.move(GPRInfo::returnValueGPR, scratchGPR);
CCallHelpers::Jump noException = jit.emitExceptionCheck(vm, CCallHelpers::InvertedExceptionCheck);
state.restoreLiveRegistersFromStackForCallWithThrownException(spillState);
state.emitExplicitExceptionHandler();
noException.link(&jit);
RegisterSet resultRegisterToExclude;
resultRegisterToExclude.set(scratchGPR);
state.restoreLiveRegistersFromStackForCall(spillState, resultRegisterToExclude);
}
}
if (isInlineOffset(m_offset)) {
jit.storeValue(
valueRegs,
CCallHelpers::Address(
baseGPR,
JSObject::offsetOfInlineStorage() +
offsetInInlineStorage(m_offset) * sizeof(JSValue)));
} else {
if (!allocating)
jit.loadPtr(CCallHelpers::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR);
jit.storeValue(
valueRegs,
CCallHelpers::Address(scratchGPR, offsetInButterfly(m_offset) * sizeof(JSValue)));
}
if (allocatingInline) {
jit.nukeStructureAndStoreButterfly(vm, scratchGPR, baseGPR);
}
uint32_t structureBits = bitwise_cast<uint32_t>(newStructure()->id());
jit.store32(
CCallHelpers::TrustedImm32(structureBits),
CCallHelpers::Address(baseGPR, JSCell::structureIDOffset()));
allocator.restoreReusedRegistersByPopping(jit, preservedState);
state.succeed();
if (allocatingInline) {
if (allocator.didReuseRegisters()) {
slowPath.link(&jit);
allocator.restoreReusedRegistersByPopping(jit, preservedState);
state.failAndIgnore.append(jit.jump());
} else
state.failAndIgnore.append(slowPath);
} else
RELEASE_ASSERT(slowPath.empty());
return;
}
case ArrayLength: {
jit.loadPtr(CCallHelpers::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR);
jit.load32(CCallHelpers::Address(scratchGPR, ArrayStorage::lengthOffset()), scratchGPR);
state.failAndIgnore.append(
jit.branch32(CCallHelpers::LessThan, scratchGPR, CCallHelpers::TrustedImm32(0)));
jit.boxInt32(scratchGPR, valueRegs);
state.succeed();
return;
}
case StringLength: {
jit.load32(CCallHelpers::Address(baseGPR, JSString::offsetOfLength()), valueRegs.payloadGPR());
jit.boxInt32(valueRegs.payloadGPR(), valueRegs);
state.succeed();
return;
}
case IntrinsicGetter: {
RELEASE_ASSERT(isValidOffset(offset()));
Structure* currStructure;
if (m_conditionSet.isEmpty())
currStructure = structure();
else
currStructure = m_conditionSet.slotBaseCondition().object()->structure();
currStructure->startWatchingPropertyForReplacements(vm, offset());
this->as<IntrinsicGetterAccessCase>().emitIntrinsicGetter(state);
return;
}
case DirectArgumentsLength:
case ScopedArgumentsLength:
case ModuleNamespaceLoad:
RELEASE_ASSERT_NOT_REACHED();
}
RELEASE_ASSERT_NOT_REACHED();
}
}
#endif