-void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure* oldStructure, Structure* newStructure, PropertyOffset cachedOffset, StructureChain* chain, ReturnAddressPtr returnAddress, bool direct)
-{
- // The code below assumes that regT0 contains the basePayload and regT1 contains the baseTag. Restore them from the stack.
-#if CPU(MIPS) || CPU(SH4) || CPU(ARM)
- // For MIPS, we don't add sizeof(void*) to the stack offset.
- load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT0);
- // For MIPS, we don't add sizeof(void*) to the stack offset.
- load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT1);
-#else
- load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[0]) + sizeof(void*) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT0);
- load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[0]) + sizeof(void*) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT1);
-#endif
-
- JumpList failureCases;
- failureCases.append(branch32(NotEqual, regT1, TrustedImm32(JSValue::CellTag)));
- failureCases.append(branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), TrustedImmPtr(oldStructure)));
- testPrototype(oldStructure->storedPrototype(), failureCases, stubInfo);
-
- if (!direct) {
- // Verify that nothing in the prototype chain has a setter for this property.
- for (WriteBarrier<Structure>* it = chain->head(); *it; ++it)
- testPrototype((*it)->storedPrototype(), failureCases, stubInfo);
- }
-
- // If we succeed in all of our checks, and the code was optimizable, then make sure we
- // decrement the rare case counter.
-#if ENABLE(VALUE_PROFILER)
- if (m_codeBlock->canCompileWithDFG() >= DFG::MayInline) {
- sub32(
- TrustedImm32(1),
- AbsoluteAddress(&m_codeBlock->rareCaseProfileForBytecodeOffset(stubInfo->bytecodeIndex)->m_counter));
- }
-#endif
-
- // Reallocate property storage if needed.
- Call callTarget;
- bool willNeedStorageRealloc = oldStructure->outOfLineCapacity() != newStructure->outOfLineCapacity();
- if (willNeedStorageRealloc) {
- // This trampoline was called to like a JIT stub; before we can can call again we need to
- // remove the return address from the stack, to prevent the stack from becoming misaligned.
- preserveReturnAddressAfterCall(regT3);
-
- JITStubCall stubCall(this, cti_op_put_by_id_transition_realloc);
- stubCall.skipArgument(); // base
- stubCall.skipArgument(); // ident
- stubCall.skipArgument(); // value
- stubCall.addArgument(TrustedImm32(oldStructure->outOfLineCapacity()));
- stubCall.addArgument(TrustedImmPtr(newStructure));
- stubCall.call(regT0);
-
- restoreReturnAddressBeforeReturn(regT3);
-
-#if CPU(MIPS) || CPU(SH4) || CPU(ARM)
- // For MIPS, we don't add sizeof(void*) to the stack offset.
- load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT0);
- // For MIPS, we don't add sizeof(void*) to the stack offset.
- load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT1);
-#else
- load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[0]) + sizeof(void*) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT0);
- load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[0]) + sizeof(void*) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT1);
-#endif
- }
-
- emitWriteBarrier(regT0, regT1, regT1, regT3, UnconditionalWriteBarrier, WriteBarrierForPropertyAccess);
-
- storePtr(TrustedImmPtr(newStructure), Address(regT0, JSCell::structureOffset()));
-#if CPU(MIPS) || CPU(SH4) || CPU(ARM)
- // For MIPS, we don't add sizeof(void*) to the stack offset.
- load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[2]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT3);
- load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[2]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT2);
-#else
- load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[2]) + sizeof(void*) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT3);
- load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[2]) + sizeof(void*) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT2);
-#endif
- compilePutDirectOffset(regT0, regT2, regT3, cachedOffset);
-
- ret();
-
- ASSERT(!failureCases.empty());
- failureCases.link(this);
- restoreArgumentReferenceForTrampoline();
- Call failureCall = tailRecursiveCall();
-
- LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
-
- patchBuffer.link(failureCall, FunctionPtr(direct ? cti_op_put_by_id_direct_fail : cti_op_put_by_id_fail));
-
- if (willNeedStorageRealloc) {
- ASSERT(m_calls.size() == 1);
- patchBuffer.link(m_calls[0].from, FunctionPtr(cti_op_put_by_id_transition_realloc));
- }
-
- stubInfo->stubRoutine = createJITStubRoutine(
- FINALIZE_CODE(
- patchBuffer,
- ("Baseline put_by_id transition stub for %s, return point %p",
- toCString(*m_codeBlock).data(), returnAddress.value())),
- *m_vm,
- m_codeBlock->ownerExecutable(),
- willNeedStorageRealloc,
- newStructure);
- RepatchBuffer repatchBuffer(m_codeBlock);
- repatchBuffer.relinkCallerToTrampoline(returnAddress, CodeLocationLabel(stubInfo->stubRoutine->code().code()));
-}
-
-void JIT::patchGetByIdSelf(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, PropertyOffset cachedOffset, ReturnAddressPtr returnAddress)
-{
- RepatchBuffer repatchBuffer(codeBlock);
-
- // We don't want to patch more than once - in future go to cti_op_get_by_id_generic.
- // Should probably go to JITStubs::cti_op_get_by_id_fail, but that doesn't do anything interesting right now.
- repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_self_fail));
-
- // Patch the offset into the propoerty map to load from, then patch the Structure to look for.
- repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.get.structureToCompare), structure);
- repatchBuffer.setLoadInstructionIsActive(stubInfo->hotPathBegin.convertibleLoadAtOffset(stubInfo->patch.baseline.u.get.propertyStorageLoad), isOutOfLineOffset(cachedOffset));
- repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelCompactAtOffset(stubInfo->patch.baseline.u.get.displacementLabel1), offsetRelativeToPatchedStorage(cachedOffset) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)); // payload
- repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelCompactAtOffset(stubInfo->patch.baseline.u.get.displacementLabel2), offsetRelativeToPatchedStorage(cachedOffset) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)); // tag
-}
-
-void JIT::patchPutByIdReplace(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, PropertyOffset cachedOffset, ReturnAddressPtr returnAddress, bool direct)
-{
- RepatchBuffer repatchBuffer(codeBlock);
-
- // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
- // Should probably go to cti_op_put_by_id_fail, but that doesn't do anything interesting right now.
- repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(direct ? cti_op_put_by_id_direct_generic : cti_op_put_by_id_generic));
-
- // Patch the offset into the propoerty map to load from, then patch the Structure to look for.
- repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.put.structureToCompare), structure);
- repatchBuffer.setLoadInstructionIsActive(stubInfo->hotPathBegin.convertibleLoadAtOffset(stubInfo->patch.baseline.u.put.propertyStorageLoad), isOutOfLineOffset(cachedOffset));
- repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(stubInfo->patch.baseline.u.put.displacementLabel1), offsetRelativeToPatchedStorage(cachedOffset) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)); // payload
- repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(stubInfo->patch.baseline.u.put.displacementLabel2), offsetRelativeToPatchedStorage(cachedOffset) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)); // tag
-}
-
-void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress)
-{
- StructureStubInfo* stubInfo = &m_codeBlock->getStubInfo(returnAddress);
-
- // regT0 holds a JSCell*
-
- // Check for array
- loadPtr(Address(regT0, JSCell::structureOffset()), regT2);
- Jump failureCases1 = branchTest32(Zero, regT2, TrustedImm32(IsArray));
- Jump failureCases2 = branchTest32(Zero, regT2, TrustedImm32(IndexingShapeMask));
-
- // Checks out okay! - get the length from the storage
- loadPtr(Address(regT0, JSArray::butterflyOffset()), regT2);
- load32(Address(regT2, ArrayStorage::lengthOffset()), regT2);
-
- Jump failureCases3 = branch32(Above, regT2, TrustedImm32(INT_MAX));
- move(regT2, regT0);
- move(TrustedImm32(JSValue::Int32Tag), regT1);
- Jump success = jump();
-
- LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
-
- // Use the patch information to link the failure cases back to the original slow case routine.
- CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin);
- patchBuffer.link(failureCases1, slowCaseBegin);
- patchBuffer.link(failureCases2, slowCaseBegin);
- patchBuffer.link(failureCases3, slowCaseBegin);
-
- // On success return back to the hot patch code, at a point it will perform the store to dest for us.
- patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult));
-
- // Track the stub we have created so that it will be deleted later.
- stubInfo->stubRoutine = FINALIZE_CODE_FOR_STUB(
- patchBuffer,
- ("Baseline get_by_id array length stub for %s, return point %p",
- toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset(
- stubInfo->patch.baseline.u.get.putResult).executableAddress()));
-
- // Finally patch the jump to slow case back in the hot path to jump here instead.
- CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck);
- RepatchBuffer repatchBuffer(m_codeBlock);
- repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubInfo->stubRoutine->code().code()));
-
- // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
- repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_array_fail));
-}
-
-void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
-{
- // regT0 holds a JSCell*
-
- // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
- // referencing the prototype object - let's speculatively load it's table nice and early!)
- JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame));
-
- Jump failureCases1 = checkStructure(regT0, structure);
-
- // Check the prototype object's Structure had not changed.
- Jump failureCases2 = addStructureTransitionCheck(protoObject, prototypeStructure, stubInfo, regT3);
-
- bool needsStubLink = false;
- // Checks out okay!
- if (slot.cachedPropertyType() == PropertySlot::Getter) {
- needsStubLink = true;
- compileGetDirectOffset(protoObject, regT2, regT1, cachedOffset);
- JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
- stubCall.addArgument(regT1);
- stubCall.addArgument(regT0);
- stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
- stubCall.call();
- } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
- needsStubLink = true;
- JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
- stubCall.addArgument(TrustedImmPtr(protoObject));
- stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
- stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
- stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
- stubCall.call();
- } else
- compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
-
- Jump success = jump();
-
- LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
-
- // Use the patch information to link the failure cases back to the original slow case routine.
- CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin);
- patchBuffer.link(failureCases1, slowCaseBegin);
- if (failureCases2.isSet())
- patchBuffer.link(failureCases2, slowCaseBegin);
-
- // On success return back to the hot patch code, at a point it will perform the store to dest for us.
- patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult));
-
- if (needsStubLink) {
- for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
- if (iter->to)
- patchBuffer.link(iter->from, FunctionPtr(iter->to));
- }
- }
-
- // Track the stub we have created so that it will be deleted later.
- stubInfo->stubRoutine = createJITStubRoutine(
- FINALIZE_CODE(
- patchBuffer,
- ("Baseline get_by_id proto stub for %s, return point %p",
- toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset(
- stubInfo->patch.baseline.u.get.putResult).executableAddress())),
- *m_vm,
- m_codeBlock->ownerExecutable(),
- needsStubLink);
-
- // Finally patch the jump to slow case back in the hot path to jump here instead.
- CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck);
- RepatchBuffer repatchBuffer(m_codeBlock);
- repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubInfo->stubRoutine->code().code()));
-
- // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
- repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list));
-}
-
-
-void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset)
-{
- // regT0 holds a JSCell*
- Jump failureCase = checkStructure(regT0, structure);
- bool needsStubLink = false;
- bool isDirect = false;
- if (slot.cachedPropertyType() == PropertySlot::Getter) {
- needsStubLink = true;
- compileGetDirectOffset(regT0, regT2, regT1, cachedOffset);
- JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
- stubCall.addArgument(regT1);
- stubCall.addArgument(regT0);
- stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
- stubCall.call();
- } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
- needsStubLink = true;
- JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
- stubCall.addArgument(regT0);
- stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
- stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
- stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
- stubCall.call();
- } else {
- isDirect = true;
- compileGetDirectOffset(regT0, regT1, regT0, cachedOffset);
- }
-
- Jump success = jump();
-
- LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
- if (needsStubLink) {
- for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
- if (iter->to)
- patchBuffer.link(iter->from, FunctionPtr(iter->to));
- }
- }
- // Use the patch information to link the failure cases back to the original slow case routine.
- CodeLocationLabel lastProtoBegin = CodeLocationLabel(JITStubRoutine::asCodePtr(polymorphicStructures->list[currentIndex - 1].stubRoutine));
- if (!lastProtoBegin)
- lastProtoBegin = stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin);
-
- patchBuffer.link(failureCase, lastProtoBegin);
-
- // On success return back to the hot patch code, at a point it will perform the store to dest for us.
- patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult));
-
- RefPtr<JITStubRoutine> stubRoutine = createJITStubRoutine(
- FINALIZE_CODE(
- patchBuffer,
- ("Baseline get_by_id self list stub for %s, return point %p",
- toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset(
- stubInfo->patch.baseline.u.get.putResult).executableAddress())),
- *m_vm,
- m_codeBlock->ownerExecutable(),
- needsStubLink);
-
- polymorphicStructures->list[currentIndex].set(*m_vm, m_codeBlock->ownerExecutable(), stubRoutine, structure, isDirect);
-
- // Finally patch the jump to slow case back in the hot path to jump here instead.
- CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck);
- RepatchBuffer repatchBuffer(m_codeBlock);
- repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code()));
-}
-
-void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset, CallFrame* callFrame)
-{
- // regT0 holds a JSCell*
-
- // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
- // referencing the prototype object - let's speculatively load it's table nice and early!)
- JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame));
-
- // Check eax is an object of the right Structure.
- Jump failureCases1 = checkStructure(regT0, structure);
-
- // Check the prototype object's Structure had not changed.
- Jump failureCases2 = addStructureTransitionCheck(protoObject, prototypeStructure, stubInfo, regT3);
-
- bool needsStubLink = false;
- bool isDirect = false;
- if (slot.cachedPropertyType() == PropertySlot::Getter) {
- needsStubLink = true;
- compileGetDirectOffset(protoObject, regT2, regT1, cachedOffset);
- JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
- stubCall.addArgument(regT1);
- stubCall.addArgument(regT0);
- stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
- stubCall.call();
- } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
- needsStubLink = true;
- JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
- stubCall.addArgument(TrustedImmPtr(protoObject));
- stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
- stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
- stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
- stubCall.call();
- } else {
- isDirect = true;
- compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
- }
-
- Jump success = jump();
-
- LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
- if (needsStubLink) {
- for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
- if (iter->to)
- patchBuffer.link(iter->from, FunctionPtr(iter->to));
- }
- }
- // Use the patch information to link the failure cases back to the original slow case routine.
- CodeLocationLabel lastProtoBegin = CodeLocationLabel(JITStubRoutine::asCodePtr(prototypeStructures->list[currentIndex - 1].stubRoutine));
- patchBuffer.link(failureCases1, lastProtoBegin);
- if (failureCases2.isSet())
- patchBuffer.link(failureCases2, lastProtoBegin);
-
- // On success return back to the hot patch code, at a point it will perform the store to dest for us.
- patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult));
-
- RefPtr<JITStubRoutine> stubRoutine = createJITStubRoutine(
- FINALIZE_CODE(
- patchBuffer,
- ("Baseline get_by_id proto list stub for %s, return point %p",
- toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset(
- stubInfo->patch.baseline.u.get.putResult).executableAddress())),
- *m_vm,
- m_codeBlock->ownerExecutable(),
- needsStubLink);
-
- prototypeStructures->list[currentIndex].set(callFrame->vm(), m_codeBlock->ownerExecutable(), stubRoutine, structure, prototypeStructure, isDirect);
-
- // Finally patch the jump to slow case back in the hot path to jump here instead.
- CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck);
- RepatchBuffer repatchBuffer(m_codeBlock);
- repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code()));
-}
-
-void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset, CallFrame* callFrame)
-{
- // regT0 holds a JSCell*
- ASSERT(count);
-
- JumpList bucketsOfFail;
-
- // Check eax is an object of the right Structure.
- bucketsOfFail.append(checkStructure(regT0, structure));
-
- Structure* currStructure = structure;
- WriteBarrier<Structure>* it = chain->head();
- JSObject* protoObject = 0;
- for (unsigned i = 0; i < count; ++i, ++it) {
- protoObject = asObject(currStructure->prototypeForLookup(callFrame));
- currStructure = it->get();
- testPrototype(protoObject, bucketsOfFail, stubInfo);
- }
- ASSERT(protoObject);
-
- bool needsStubLink = false;
- bool isDirect = false;
- if (slot.cachedPropertyType() == PropertySlot::Getter) {
- needsStubLink = true;
- compileGetDirectOffset(protoObject, regT2, regT1, cachedOffset);
- JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
- stubCall.addArgument(regT1);
- stubCall.addArgument(regT0);
- stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
- stubCall.call();
- } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
- needsStubLink = true;
- JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
- stubCall.addArgument(TrustedImmPtr(protoObject));
- stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
- stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
- stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
- stubCall.call();
- } else {
- isDirect = true;
- compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
- }
-
- Jump success = jump();
-
- LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
- if (needsStubLink) {
- for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
- if (iter->to)
- patchBuffer.link(iter->from, FunctionPtr(iter->to));
- }
- }
- // Use the patch information to link the failure cases back to the original slow case routine.
- CodeLocationLabel lastProtoBegin = CodeLocationLabel(JITStubRoutine::asCodePtr(prototypeStructures->list[currentIndex - 1].stubRoutine));
-
- patchBuffer.link(bucketsOfFail, lastProtoBegin);
-
- // On success return back to the hot patch code, at a point it will perform the store to dest for us.
- patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult));
-
- RefPtr<JITStubRoutine> stubRoutine = createJITStubRoutine(
- FINALIZE_CODE(
- patchBuffer,
- ("Baseline get_by_id chain list stub for %s, return point %p",
- toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset(
- stubInfo->patch.baseline.u.get.putResult).executableAddress())),
- *m_vm,
- m_codeBlock->ownerExecutable(),
- needsStubLink);
-
- // Track the stub we have created so that it will be deleted later.
- prototypeStructures->list[currentIndex].set(callFrame->vm(), m_codeBlock->ownerExecutable(), stubRoutine, structure, chain, isDirect);
-
- // Finally patch the jump to slow case back in the hot path to jump here instead.
- CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck);
- RepatchBuffer repatchBuffer(m_codeBlock);
- repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code()));
-}
-
-void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
-{
- // regT0 holds a JSCell*
- ASSERT(count);
-
- JumpList bucketsOfFail;
-
- // Check eax is an object of the right Structure.
- bucketsOfFail.append(checkStructure(regT0, structure));
-
- Structure* currStructure = structure;
- WriteBarrier<Structure>* it = chain->head();
- JSObject* protoObject = 0;
- for (unsigned i = 0; i < count; ++i, ++it) {
- protoObject = asObject(currStructure->prototypeForLookup(callFrame));
- currStructure = it->get();
- testPrototype(protoObject, bucketsOfFail, stubInfo);
- }
- ASSERT(protoObject);
-
- bool needsStubLink = false;
- if (slot.cachedPropertyType() == PropertySlot::Getter) {
- needsStubLink = true;
- compileGetDirectOffset(protoObject, regT2, regT1, cachedOffset);
- JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
- stubCall.addArgument(regT1);
- stubCall.addArgument(regT0);
- stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
- stubCall.call();
- } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
- needsStubLink = true;
- JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
- stubCall.addArgument(TrustedImmPtr(protoObject));
- stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
- stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
- stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
- stubCall.call();
- } else
- compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
- Jump success = jump();
-
- LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
- if (needsStubLink) {
- for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
- if (iter->to)
- patchBuffer.link(iter->from, FunctionPtr(iter->to));
- }
- }
- // Use the patch information to link the failure cases back to the original slow case routine.
- patchBuffer.link(bucketsOfFail, stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin));
-
- // On success return back to the hot patch code, at a point it will perform the store to dest for us.
- patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult));
-
- // Track the stub we have created so that it will be deleted later.
- RefPtr<JITStubRoutine> stubRoutine = createJITStubRoutine(
- FINALIZE_CODE(
- patchBuffer,
- ("Baseline get_by_id chain stub for %s, return point %p",
- toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset(
- stubInfo->patch.baseline.u.get.putResult).executableAddress())),
- *m_vm,
- m_codeBlock->ownerExecutable(),
- needsStubLink);
- stubInfo->stubRoutine = stubRoutine;
-
- // Finally patch the jump to slow case back in the hot path to jump here instead.
- CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck);
- RepatchBuffer repatchBuffer(m_codeBlock);
- repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code()));
-
- // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
- repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list));
-}
-