diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-05 14:34:32 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-05 14:34:32 -0800 |
commit | 635860845790a19bf50bbc51ba8fb66a96dde068 (patch) | |
tree | ef6ad9ff73a5b57f65249d4232a202fa77e6a140 /JavaScriptCore/jit/JITPropertyAccess.cpp | |
parent | 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (diff) | |
download | external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.zip external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.tar.gz external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.tar.bz2 |
auto import from //depot/cupcake/@136594
Diffstat (limited to 'JavaScriptCore/jit/JITPropertyAccess.cpp')
-rw-r--r-- | JavaScriptCore/jit/JITPropertyAccess.cpp | 704 |
1 files changed, 704 insertions, 0 deletions
diff --git a/JavaScriptCore/jit/JITPropertyAccess.cpp b/JavaScriptCore/jit/JITPropertyAccess.cpp new file mode 100644 index 0000000..6740bec --- /dev/null +++ b/JavaScriptCore/jit/JITPropertyAccess.cpp @@ -0,0 +1,704 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JIT.h" + +#if ENABLE(JIT) + +#include "CodeBlock.h" +#include "JITInlineMethods.h" +#include "JSArray.h" +#include "JSFunction.h" +#include "Interpreter.h" +#include "ResultType.h" +#include "SamplingTool.h" + +#ifndef NDEBUG +#include <stdio.h> +#endif + +using namespace std; + +namespace JSC { + +#if !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) + +void JIT::compileGetByIdHotPath(int resultVReg, int baseVReg, Identifier* ident, unsigned) +{ + // As for put_by_id, get_by_id requires the offset of the Structure and the offset of the access to be patched. + // Additionally, for get_by_id we need patch the offset of the branch to the slow case (we patch this to jump + // to array-length / prototype access tranpolines, and finally we also the the property-map access offset as a label + // to jump back to if one of these trampolies finds a match. + + emitGetVirtualRegister(baseVReg, X86::eax); + + emitPutJITStubArg(X86::eax, 1); + emitPutJITStubArgConstant(ident, 2); + emitCTICall(Interpreter::cti_op_get_by_id_generic); + emitPutVirtualRegister(resultVReg); +} + + +void JIT::compileGetByIdSlowCase(int, int, Identifier*, Vector<SlowCaseEntry>::iterator&, unsigned) +{ + ASSERT_NOT_REACHED(); +} + +void JIT::compilePutByIdHotPath(int baseVReg, Identifier* ident, int valueVReg, unsigned) +{ + // In order to be able to patch both the Structure, and the object offset, we store one pointer, + // to just after the arguments have been loaded into registers 'hotPathBegin', and we generate code + // such that the Structure & offset are always at the same distance from this. + + emitGetVirtualRegisters(baseVReg, X86::eax, valueVReg, X86::edx); + + emitPutJITStubArgConstant(ident, 2); + emitPutJITStubArg(X86::eax, 1); + emitPutJITStubArg(X86::edx, 3); + emitCTICall(Interpreter::cti_op_put_by_id_generic); +} + +void JIT::compilePutByIdSlowCase(int, Identifier*, int, Vector<SlowCaseEntry>::iterator&, unsigned) +{ + ASSERT_NOT_REACHED(); +} + +#else + +void JIT::compileGetByIdHotPath(int resultVReg, int baseVReg, Identifier*, unsigned propertyAccessInstructionIndex) +{ + // As for put_by_id, get_by_id requires the offset of the Structure and the offset of the access to be patched. + // Additionally, for get_by_id we need patch the offset of the branch to the slow case (we patch this to jump + // to array-length / prototype access tranpolines, and finally we also the the property-map access offset as a label + // to jump back to if one of these trampolies finds a match. + + emitGetVirtualRegister(baseVReg, X86::eax); + + emitJumpSlowCaseIfNotJSCell(X86::eax, baseVReg); + + Label hotPathBegin(this); + m_propertyAccessCompilationInfo[propertyAccessInstructionIndex].hotPathBegin = hotPathBegin; + + DataLabelPtr structureToCompare; + Jump structureCheck = jnePtrWithPatch(Address(X86::eax, FIELD_OFFSET(JSCell, m_structure)), structureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure))); + addSlowCase(structureCheck); + ASSERT(differenceBetween(hotPathBegin, structureToCompare) == patchOffsetGetByIdStructure); + ASSERT(differenceBetween(hotPathBegin, structureCheck) == patchOffsetGetByIdBranchToSlowCase); + + loadPtr(Address(X86::eax, FIELD_OFFSET(JSObject, m_propertyStorage)), X86::eax); + DataLabel32 displacementLabel = loadPtrWithAddressOffsetPatch(Address(X86::eax, patchGetByIdDefaultOffset), X86::eax); + ASSERT(differenceBetween(hotPathBegin, displacementLabel) == patchOffsetGetByIdPropertyMapOffset); + + Label putResult(this); + ASSERT(differenceBetween(hotPathBegin, putResult) == patchOffsetGetByIdPutResult); + emitPutVirtualRegister(resultVReg); +} + + +void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident, Vector<SlowCaseEntry>::iterator& iter, unsigned propertyAccessInstructionIndex) +{ + // As for the hot path of get_by_id, above, we ensure that we can use an architecture specific offset + // so that we only need track one pointer into the slow case code - we track a pointer to the location + // of the call (which we can use to look up the patch information), but should a array-length or + // prototype access trampoline fail we want to bail out back to here. To do so we can subtract back + // the distance from the call to the head of the slow case. + + linkSlowCaseIfNotJSCell(iter, baseVReg); + linkSlowCase(iter); + +#ifndef NDEBUG + Label coldPathBegin(this); +#endif + emitPutJITStubArg(X86::eax, 1); + emitPutJITStubArgConstant(ident, 2); + Jump call = emitCTICall(Interpreter::cti_op_get_by_id); + emitPutVirtualRegister(resultVReg); + + ASSERT(differenceBetween(coldPathBegin, call) == patchOffsetGetByIdSlowCaseCall); + + // Track the location of the call; this will be used to recover patch information. + m_propertyAccessCompilationInfo[propertyAccessInstructionIndex].callReturnLocation = call; +} + +void JIT::compilePutByIdHotPath(int baseVReg, Identifier*, int valueVReg, unsigned propertyAccessInstructionIndex) +{ + // In order to be able to patch both the Structure, and the object offset, we store one pointer, + // to just after the arguments have been loaded into registers 'hotPathBegin', and we generate code + // such that the Structure & offset are always at the same distance from this. + + emitGetVirtualRegisters(baseVReg, X86::eax, valueVReg, X86::edx); + + // Jump to a slow case if either the base object is an immediate, or if the Structure does not match. + emitJumpSlowCaseIfNotJSCell(X86::eax, baseVReg); + + Label hotPathBegin(this); + m_propertyAccessCompilationInfo[propertyAccessInstructionIndex].hotPathBegin = hotPathBegin; + + // It is important that the following instruction plants a 32bit immediate, in order that it can be patched over. + DataLabelPtr structureToCompare; + addSlowCase(jnePtrWithPatch(Address(X86::eax, FIELD_OFFSET(JSCell, m_structure)), structureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure)))); + ASSERT(differenceBetween(hotPathBegin, structureToCompare) == patchOffsetPutByIdStructure); + + // Plant a load from a bogus ofset in the object's property map; we will patch this later, if it is to be used. + loadPtr(Address(X86::eax, FIELD_OFFSET(JSObject, m_propertyStorage)), X86::eax); + DataLabel32 displacementLabel = storePtrWithAddressOffsetPatch(X86::edx, Address(X86::eax, patchGetByIdDefaultOffset)); + ASSERT(differenceBetween(hotPathBegin, displacementLabel) == patchOffsetPutByIdPropertyMapOffset); +} + +void JIT::compilePutByIdSlowCase(int baseVReg, Identifier* ident, int, Vector<SlowCaseEntry>::iterator& iter, unsigned propertyAccessInstructionIndex) +{ + linkSlowCaseIfNotJSCell(iter, baseVReg); + linkSlowCase(iter); + + emitPutJITStubArgConstant(ident, 2); + emitPutJITStubArg(X86::eax, 1); + emitPutJITStubArg(X86::edx, 3); + Jump call = emitCTICall(Interpreter::cti_op_put_by_id); + + // Track the location of the call; this will be used to recover patch information. + m_propertyAccessCompilationInfo[propertyAccessInstructionIndex].callReturnLocation = call; +} + +static JSObject* resizePropertyStorage(JSObject* baseObject, int32_t oldSize, int32_t newSize) +{ + baseObject->allocatePropertyStorage(oldSize, newSize); + return baseObject; +} + +static inline bool transitionWillNeedStorageRealloc(Structure* oldStructure, Structure* newStructure) +{ + return oldStructure->propertyStorageCapacity() != newStructure->propertyStorageCapacity(); +} + +void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure* oldStructure, Structure* newStructure, size_t cachedOffset, StructureChain* chain, void* returnAddress) +{ + JumpList failureCases; + // Check eax is an object of the right Structure. + failureCases.append(emitJumpIfNotJSCell(X86::eax)); + failureCases.append(jnePtr(Address(X86::eax, FIELD_OFFSET(JSCell, m_structure)), ImmPtr(oldStructure))); + JumpList successCases; + + // ecx = baseObject + loadPtr(Address(X86::eax, FIELD_OFFSET(JSCell, m_structure)), X86::ecx); + // proto(ecx) = baseObject->structure()->prototype() + failureCases.append(jne32(Address(X86::ecx, FIELD_OFFSET(Structure, m_typeInfo) + FIELD_OFFSET(TypeInfo, m_type)), Imm32(ObjectType))); + + loadPtr(Address(X86::ecx, FIELD_OFFSET(Structure, m_prototype)), X86::ecx); + + // ecx = baseObject->m_structure + for (RefPtr<Structure>* it = chain->head(); *it; ++it) { + // null check the prototype + successCases.append(jePtr(X86::ecx, ImmPtr(JSValuePtr::encode(jsNull())))); + + // Check the structure id + failureCases.append(jnePtr(Address(X86::ecx, FIELD_OFFSET(JSCell, m_structure)), ImmPtr(it->get()))); + + loadPtr(Address(X86::ecx, FIELD_OFFSET(JSCell, m_structure)), X86::ecx); + failureCases.append(jne32(Address(X86::ecx, FIELD_OFFSET(Structure, m_typeInfo) + FIELD_OFFSET(TypeInfo, m_type)), Imm32(ObjectType))); + loadPtr(Address(X86::ecx, FIELD_OFFSET(Structure, m_prototype)), X86::ecx); + } + + successCases.link(this); + + Jump callTarget; + + // emit a call only if storage realloc is needed + if (transitionWillNeedStorageRealloc(oldStructure, newStructure)) { + pop(X86::ebx); +#if PLATFORM(X86_64) + move(Imm32(newStructure->propertyStorageCapacity()), X86::edx); + move(Imm32(oldStructure->propertyStorageCapacity()), X86::esi); + move(X86::eax, X86::edi); + callTarget = call(); +#else + push(Imm32(newStructure->propertyStorageCapacity())); + push(Imm32(oldStructure->propertyStorageCapacity())); + push(X86::eax); + callTarget = call(); + addPtr(Imm32(3 * sizeof(void*)), X86::esp); +#endif + emitGetJITStubArg(3, X86::edx); + push(X86::ebx); + } + + // Assumes m_refCount can be decremented easily, refcount decrement is safe as + // codeblock should ensure oldStructure->m_refCount > 0 + sub32(Imm32(1), AbsoluteAddress(oldStructure->addressOfCount())); + add32(Imm32(1), AbsoluteAddress(newStructure->addressOfCount())); + storePtr(ImmPtr(newStructure), Address(X86::eax, FIELD_OFFSET(JSCell, m_structure))); + + // write the value + loadPtr(Address(X86::eax, FIELD_OFFSET(JSObject, m_propertyStorage)), X86::eax); + storePtr(X86::edx, Address(X86::eax, cachedOffset * sizeof(JSValuePtr))); + + ret(); + + Jump failureJump; + bool plantedFailureJump = false; + if (!failureCases.empty()) { + failureCases.link(this); + restoreArgumentReferenceForTrampoline(); + failureJump = jump(); + plantedFailureJump = true; + } + + void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); + PatchBuffer patchBuffer(code); + + if (plantedFailureJump) + patchBuffer.link(failureJump, reinterpret_cast<void*>(Interpreter::cti_op_put_by_id_fail)); + + if (transitionWillNeedStorageRealloc(oldStructure, newStructure)) + patchBuffer.link(callTarget, reinterpret_cast<void*>(resizePropertyStorage)); + + stubInfo->stubRoutine = code; + + Jump::patch(returnAddress, code); +} + +void JIT::patchGetByIdSelf(StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, void* returnAddress) +{ + // We don't want to patch more than once - in future go to cti_op_get_by_id_generic. + // Should probably go to Interpreter::cti_op_get_by_id_fail, but that doesn't do anything interesting right now. + Jump::patch(returnAddress, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_self_fail)); + + // Patch the offset into the propoerty map to load from, then patch the Structure to look for. + void* structureAddress = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(stubInfo->hotPathBegin) + patchOffsetGetByIdStructure); + void* displacementAddress = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(stubInfo->hotPathBegin) + patchOffsetGetByIdPropertyMapOffset); + DataLabelPtr::patch(structureAddress, structure); + DataLabel32::patch(displacementAddress, cachedOffset * sizeof(JSValuePtr)); +} + +void JIT::patchPutByIdReplace(StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, void* returnAddress) +{ + // We don't want to patch more than once - in future go to cti_op_put_by_id_generic. + // Should probably go to Interpreter::cti_op_put_by_id_fail, but that doesn't do anything interesting right now. + Jump::patch(returnAddress, reinterpret_cast<void*>(Interpreter::cti_op_put_by_id_generic)); + + // Patch the offset into the propoerty map to load from, then patch the Structure to look for. + void* structureAddress = reinterpret_cast<char*>(stubInfo->hotPathBegin) + patchOffsetPutByIdStructure; + void* displacementAddress = reinterpret_cast<char*>(stubInfo->hotPathBegin) + patchOffsetPutByIdPropertyMapOffset; + DataLabelPtr::patch(structureAddress, structure); + DataLabel32::patch(displacementAddress, cachedOffset * sizeof(JSValuePtr)); +} + +void JIT::privateCompilePatchGetArrayLength(void* returnAddress) +{ + StructureStubInfo* stubInfo = &m_codeBlock->getStubInfo(returnAddress); + + // We don't want to patch more than once - in future go to cti_op_put_by_id_generic. + Jump::patch(returnAddress, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_array_fail)); + + // Check eax is an array + Jump failureCases1 = jnePtr(Address(X86::eax), ImmPtr(m_interpreter->m_jsArrayVptr)); + + // Checks out okay! - get the length from the storage + loadPtr(Address(X86::eax, FIELD_OFFSET(JSArray, m_storage)), X86::ecx); + load32(Address(X86::ecx, FIELD_OFFSET(ArrayStorage, m_length)), X86::ecx); + + Jump failureCases2 = ja32(X86::ecx, Imm32(JSImmediate::maxImmediateInt)); + + emitFastArithIntToImmNoCheck(X86::ecx, X86::eax); + Jump success = jump(); + + void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); + PatchBuffer patchBuffer(code); + + // Use the patch information to link the failure cases back to the original slow case routine. + void* slowCaseBegin = reinterpret_cast<char*>(stubInfo->callReturnLocation) - patchOffsetGetByIdSlowCaseCall; + patchBuffer.link(failureCases1, slowCaseBegin); + 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. + void* hotPathPutResult = reinterpret_cast<char*>(stubInfo->hotPathBegin) + patchOffsetGetByIdPutResult; + patchBuffer.link(success, hotPathPutResult); + + // Track the stub we have created so that it will be deleted later. + stubInfo->stubRoutine = code; + + // Finally patch the jump to sow case back in the hot path to jump here instead. + void* jumpLocation = reinterpret_cast<char*>(stubInfo->hotPathBegin) + patchOffsetGetByIdBranchToSlowCase; + Jump::patch(jumpLocation, code); +} + +void JIT::privateCompileGetByIdSelf(StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, void* returnAddress) +{ + // Check eax is an object of the right Structure. + Jump failureCases1 = emitJumpIfNotJSCell(X86::eax); + Jump failureCases2 = checkStructure(X86::eax, structure); + + // Checks out okay! - getDirectOffset + loadPtr(Address(X86::eax, FIELD_OFFSET(JSObject, m_propertyStorage)), X86::eax); + loadPtr(Address(X86::eax, cachedOffset * sizeof(JSValuePtr)), X86::eax); + ret(); + + void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); + PatchBuffer patchBuffer(code); + + patchBuffer.link(failureCases1, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_self_fail)); + patchBuffer.link(failureCases2, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_self_fail)); + + stubInfo->stubRoutine = code; + + Jump::patch(returnAddress, code); +} + +void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, void* returnAddress, CallFrame* callFrame) +{ +#if USE(CTI_REPATCH_PIC) + // We don't want to patch more than once - in future go to cti_op_put_by_id_generic. + Jump::patch(returnAddress, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_list)); + + // 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)); + PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage; + loadPtr(static_cast<void*>(protoPropertyStorage), X86::edx); + + // Check eax is an object of the right Structure. + Jump failureCases1 = checkStructure(X86::eax, structure); + + // Check the prototype object's Structure had not changed. + Structure** prototypeStructureAddress = &(protoObject->m_structure); +#if PLATFORM(X86_64) + move(ImmPtr(prototypeStructure), X86::ebx); + Jump failureCases2 = jnePtr(X86::ebx, AbsoluteAddress(prototypeStructureAddress)); +#else + Jump failureCases2 = jnePtr(AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure)); +#endif + + // Checks out okay! - getDirectOffset + loadPtr(Address(X86::edx, cachedOffset * sizeof(JSValuePtr)), X86::eax); + + Jump success = jump(); + + void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); + PatchBuffer patchBuffer(code); + + // Use the patch information to link the failure cases back to the original slow case routine. + void* slowCaseBegin = reinterpret_cast<char*>(stubInfo->callReturnLocation) - patchOffsetGetByIdSlowCaseCall; + patchBuffer.link(failureCases1, slowCaseBegin); + 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. + intptr_t successDest = reinterpret_cast<intptr_t>(stubInfo->hotPathBegin) + patchOffsetGetByIdPutResult; + patchBuffer.link(success, reinterpret_cast<void*>(successDest)); + + // Track the stub we have created so that it will be deleted later. + stubInfo->stubRoutine = code; + + // Finally patch the jump to slow case back in the hot path to jump here instead. + void* jumpLocation = reinterpret_cast<char*>(stubInfo->hotPathBegin) + patchOffsetGetByIdBranchToSlowCase; + Jump::patch(jumpLocation, code); +#else + // 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)); + PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage; + loadPtr(protoPropertyStorage, X86::edx); + + // Check eax is an object of the right Structure. + Jump failureCases1 = emitJumpIfNotJSCell(X86::eax); + Jump failureCases2 = checkStructure(X86::eax, structure); + + // Check the prototype object's Structure had not changed. + Structure** prototypeStructureAddress = &(protoObject->m_structure); + Jump failureCases3 = jnePtr(AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure)); + + // Checks out okay! - getDirectOffset + loadPtr(Address(X86::edx, cachedOffset * sizeof(JSValuePtr)), X86::eax); + + ret(); + + void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); + PatchBuffer patchBuffer(code); + + patchBuffer.link(failureCases1, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_fail)); + patchBuffer.link(failureCases2, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_fail)); + patchBuffer.link(failureCases3, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_fail)); + + stubInfo->stubRoutine = code; + + Jump::patch(returnAddress, code); +#endif +} + +#if USE(CTI_REPATCH_PIC) +void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, size_t cachedOffset) +{ + Jump failureCase = checkStructure(X86::eax, structure); + loadPtr(Address(X86::eax, FIELD_OFFSET(JSObject, m_propertyStorage)), X86::eax); + loadPtr(Address(X86::eax, cachedOffset * sizeof(JSValuePtr)), X86::eax); + Jump success = jump(); + + void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); + ASSERT(code); + PatchBuffer patchBuffer(code); + + // Use the patch information to link the failure cases back to the original slow case routine. + void* lastProtoBegin = polymorphicStructures->list[currentIndex - 1].stubRoutine; + if (!lastProtoBegin) + lastProtoBegin = reinterpret_cast<char*>(stubInfo->callReturnLocation) - patchOffsetGetByIdSlowCaseCall; + + 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. + intptr_t successDest = reinterpret_cast<intptr_t>(stubInfo->hotPathBegin) + patchOffsetGetByIdPutResult; + patchBuffer.link(success, reinterpret_cast<void*>(successDest)); + + structure->ref(); + polymorphicStructures->list[currentIndex].set(code, structure); + + // Finally patch the jump to slow case back in the hot path to jump here instead. + void* jumpLocation = reinterpret_cast<char*>(stubInfo->hotPathBegin) + patchOffsetGetByIdBranchToSlowCase; + Jump::patch(jumpLocation, code); +} + +void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, CallFrame* callFrame) +{ + // 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)); + PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage; + loadPtr(protoPropertyStorage, X86::edx); + + // Check eax is an object of the right Structure. + Jump failureCases1 = checkStructure(X86::eax, structure); + + // Check the prototype object's Structure had not changed. + Structure** prototypeStructureAddress = &(protoObject->m_structure); +#if PLATFORM(X86_64) + move(ImmPtr(prototypeStructure), X86::ebx); + Jump failureCases2 = jnePtr(X86::ebx, AbsoluteAddress(prototypeStructureAddress)); +#else + Jump failureCases2 = jnePtr(AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure)); +#endif + + // Checks out okay! - getDirectOffset + loadPtr(Address(X86::edx, cachedOffset * sizeof(JSValuePtr)), X86::eax); + + Jump success = jump(); + + void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); + PatchBuffer patchBuffer(code); + + // Use the patch information to link the failure cases back to the original slow case routine. + void* lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine; + patchBuffer.link(failureCases1, lastProtoBegin); + 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. + intptr_t successDest = reinterpret_cast<intptr_t>(stubInfo->hotPathBegin) + patchOffsetGetByIdPutResult; + patchBuffer.link(success, reinterpret_cast<void*>(successDest)); + + structure->ref(); + prototypeStructure->ref(); + prototypeStructures->list[currentIndex].set(code, structure, prototypeStructure); + + // Finally patch the jump to slow case back in the hot path to jump here instead. + void* jumpLocation = reinterpret_cast<char*>(stubInfo->hotPathBegin) + patchOffsetGetByIdBranchToSlowCase; + Jump::patch(jumpLocation, code); +} + +void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, CallFrame* callFrame) +{ + ASSERT(count); + + JumpList bucketsOfFail; + + // Check eax is an object of the right Structure. + Jump baseObjectCheck = checkStructure(X86::eax, structure); + bucketsOfFail.append(baseObjectCheck); + + Structure* currStructure = structure; + RefPtr<Structure>* chainEntries = chain->head(); + JSObject* protoObject = 0; + for (unsigned i = 0; i < count; ++i) { + protoObject = asObject(currStructure->prototypeForLookup(callFrame)); + currStructure = chainEntries[i].get(); + + // Check the prototype object's Structure had not changed. + Structure** prototypeStructureAddress = &(protoObject->m_structure); +#if PLATFORM(X86_64) + move(ImmPtr(currStructure), X86::ebx); + bucketsOfFail.append(jnePtr(X86::ebx, AbsoluteAddress(prototypeStructureAddress))); +#else + bucketsOfFail.append(jnePtr(AbsoluteAddress(prototypeStructureAddress), ImmPtr(currStructure))); +#endif + } + ASSERT(protoObject); + + PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage; + loadPtr(protoPropertyStorage, X86::edx); + loadPtr(Address(X86::edx, cachedOffset * sizeof(JSValuePtr)), X86::eax); + Jump success = jump(); + + void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); + PatchBuffer patchBuffer(code); + + // Use the patch information to link the failure cases back to the original slow case routine. + void* lastProtoBegin = 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. + intptr_t successDest = reinterpret_cast<intptr_t>(stubInfo->hotPathBegin) + patchOffsetGetByIdPutResult; + patchBuffer.link(success, reinterpret_cast<void*>(successDest)); + + // Track the stub we have created so that it will be deleted later. + structure->ref(); + chain->ref(); + prototypeStructures->list[currentIndex].set(code, structure, chain); + + // Finally patch the jump to slow case back in the hot path to jump here instead. + void* jumpLocation = reinterpret_cast<char*>(stubInfo->hotPathBegin) + patchOffsetGetByIdBranchToSlowCase; + Jump::patch(jumpLocation, code); +} +#endif + +void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, void* returnAddress, CallFrame* callFrame) +{ +#if USE(CTI_REPATCH_PIC) + // We don't want to patch more than once - in future go to cti_op_put_by_id_generic. + Jump::patch(returnAddress, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_list)); + + ASSERT(count); + + JumpList bucketsOfFail; + + // Check eax is an object of the right Structure. + bucketsOfFail.append(checkStructure(X86::eax, structure)); + + Structure* currStructure = structure; + RefPtr<Structure>* chainEntries = chain->head(); + JSObject* protoObject = 0; + for (unsigned i = 0; i < count; ++i) { + protoObject = asObject(currStructure->prototypeForLookup(callFrame)); + currStructure = chainEntries[i].get(); + + // Check the prototype object's Structure had not changed. + Structure** prototypeStructureAddress = &(protoObject->m_structure); +#if PLATFORM(X86_64) + move(ImmPtr(currStructure), X86::ebx); + bucketsOfFail.append(jnePtr(X86::ebx, AbsoluteAddress(prototypeStructureAddress))); +#else + bucketsOfFail.append(jnePtr(AbsoluteAddress(prototypeStructureAddress), ImmPtr(currStructure))); +#endif + } + ASSERT(protoObject); + + PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage; + loadPtr(protoPropertyStorage, X86::edx); + loadPtr(Address(X86::edx, cachedOffset * sizeof(JSValuePtr)), X86::eax); + Jump success = jump(); + + void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); + PatchBuffer patchBuffer(code); + + // Use the patch information to link the failure cases back to the original slow case routine. + void* slowCaseBegin = reinterpret_cast<char*>(stubInfo->callReturnLocation) - patchOffsetGetByIdSlowCaseCall; + + patchBuffer.link(bucketsOfFail, slowCaseBegin); + + // On success return back to the hot patch code, at a point it will perform the store to dest for us. + intptr_t successDest = reinterpret_cast<intptr_t>(stubInfo->hotPathBegin) + patchOffsetGetByIdPutResult; + patchBuffer.link(success, reinterpret_cast<void*>(successDest)); + + // Track the stub we have created so that it will be deleted later. + stubInfo->stubRoutine = code; + + // Finally patch the jump to slow case back in the hot path to jump here instead. + void* jumpLocation = reinterpret_cast<char*>(stubInfo->hotPathBegin) + patchOffsetGetByIdBranchToSlowCase; + Jump::patch(jumpLocation, code); +#else + ASSERT(count); + + JumpList bucketsOfFail; + + // Check eax is an object of the right Structure. + bucketsOfFail.append(emitJumpIfNotJSCell(X86::eax)); + bucketsOfFail.append(checkStructure(X86::eax, structure)); + + Structure* currStructure = structure; + RefPtr<Structure>* chainEntries = chain->head(); + JSObject* protoObject = 0; + for (unsigned i = 0; i < count; ++i) { + protoObject = asObject(currStructure->prototypeForLookup(callFrame)); + currStructure = chainEntries[i].get(); + + // Check the prototype object's Structure had not changed. + Structure** prototypeStructureAddress = &(protoObject->m_structure); +#if PLATFORM(X86_64) + move(ImmPtr(currStructure), X86::ebx); + bucketsOfFail.append(jnePtr(X86::ebx, AbsoluteAddress(prototypeStructureAddress))); +#else + bucketsOfFail.append(jnePtr(AbsoluteAddress(prototypeStructureAddress), ImmPtr(currStructure))); +#endif + } + ASSERT(protoObject); + + PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage; + loadPtr(protoPropertyStorage, X86::edx); + loadPtr(Address(X86::edx, cachedOffset * sizeof(JSValuePtr)), X86::eax); + ret(); + + void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); + + patchBuffer.link(bucketsOfFail, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_proto_fail)); + + stubInfo->stubRoutine = code; + + Jump::patch(returnAddress, code); +#endif +} + +void JIT::privateCompilePutByIdReplace(StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, void* returnAddress) +{ + // Check eax is an object of the right Structure. + Jump failureCases1 = emitJumpIfNotJSCell(X86::eax); + Jump failureCases2 = checkStructure(X86::eax, structure); + + // checks out okay! - putDirectOffset + loadPtr(Address(X86::eax, FIELD_OFFSET(JSObject, m_propertyStorage)), X86::eax); + storePtr(X86::edx, Address(X86::eax, cachedOffset * sizeof(JSValuePtr))); + ret(); + + void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); + PatchBuffer patchBuffer(code); + + patchBuffer.link(failureCases1, reinterpret_cast<void*>(Interpreter::cti_op_put_by_id_fail)); + patchBuffer.link(failureCases2, reinterpret_cast<void*>(Interpreter::cti_op_put_by_id_fail)); + + stubInfo->stubRoutine = code; + + Jump::patch(returnAddress, code); +} + +#endif + +} // namespace JSC + +#endif // ENABLE(JIT) |