diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2009-06-15 09:06:43 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-06-15 09:31:31 (GMT) |
commit | c411f16870f112c3407c28c22b617f613a82cff4 (patch) | |
tree | 29a1bcd590c8b31af2aab445bfe8a978dc5bf582 /src/3rdparty/webkit/JavaScriptCore/jit/JITPropertyAccess.cpp | |
parent | 3d77b56b32a0c53ec0bbfaa07236fedb900ff336 (diff) | |
download | Qt-c411f16870f112c3407c28c22b617f613a82cff4.zip Qt-c411f16870f112c3407c28c22b617f613a82cff4.tar.gz Qt-c411f16870f112c3407c28c22b617f613a82cff4.tar.bz2 |
Updated WebKit from /home/shausman/src/webkit/trunk to qtwebkit-4.6-snapshot-15062009 ( 65232bf00dc494ebfd978f998c88f58d18ecce1e )
Diffstat (limited to 'src/3rdparty/webkit/JavaScriptCore/jit/JITPropertyAccess.cpp')
-rw-r--r-- | src/3rdparty/webkit/JavaScriptCore/jit/JITPropertyAccess.cpp | 774 |
1 files changed, 444 insertions, 330 deletions
diff --git a/src/3rdparty/webkit/JavaScriptCore/jit/JITPropertyAccess.cpp b/src/3rdparty/webkit/JavaScriptCore/jit/JITPropertyAccess.cpp index 6740bec..29b9cab 100644 --- a/src/3rdparty/webkit/JavaScriptCore/jit/JITPropertyAccess.cpp +++ b/src/3rdparty/webkit/JavaScriptCore/jit/JITPropertyAccess.cpp @@ -30,6 +30,7 @@ #include "CodeBlock.h" #include "JITInlineMethods.h" +#include "JITStubCall.h" #include "JSArray.h" #include "JSFunction.h" #include "Interpreter.h" @@ -44,81 +45,274 @@ using namespace std; namespace JSC { -#if !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) +void JIT::emit_op_get_by_val(Instruction* currentInstruction) +{ + emitGetVirtualRegisters(currentInstruction[2].u.operand, regT0, currentInstruction[3].u.operand, regT1); + emitJumpSlowCaseIfNotImmediateInteger(regT1); +#if USE(ALTERNATE_JSIMMEDIATE) + // This is technically incorrect - we're zero-extending an int32. On the hot path this doesn't matter. + // We check the value as if it was a uint32 against the m_fastAccessCutoff - which will always fail if + // number was signed since m_fastAccessCutoff is always less than intmax (since the total allocation + // size is always less than 4Gb). As such zero extending wil have been correct (and extending the value + // to 64-bits is necessary since it's used in the address calculation. We zero extend rather than sign + // extending since it makes it easier to re-tag the value in the slow case. + zeroExtend32ToPtr(regT1, regT1); +#else + emitFastArithImmToInt(regT1); +#endif + emitJumpSlowCaseIfNotJSCell(regT0); + addSlowCase(branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr))); + + // This is an array; get the m_storage pointer into ecx, then check if the index is below the fast cutoff + loadPtr(Address(regT0, FIELD_OFFSET(JSArray, m_storage)), regT2); + addSlowCase(branch32(AboveOrEqual, regT1, Address(regT0, FIELD_OFFSET(JSArray, m_fastAccessCutoff)))); -void JIT::compileGetByIdHotPath(int resultVReg, int baseVReg, Identifier* ident, unsigned) + // Get the value from the vector + loadPtr(BaseIndex(regT2, regT1, ScalePtr, FIELD_OFFSET(ArrayStorage, m_vector[0])), regT0); + emitPutVirtualRegister(currentInstruction[1].u.operand); +} + +void JIT::emit_op_put_by_val(Instruction* currentInstruction) { - // 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. + emitGetVirtualRegisters(currentInstruction[1].u.operand, regT0, currentInstruction[2].u.operand, regT1); + emitJumpSlowCaseIfNotImmediateInteger(regT1); +#if USE(ALTERNATE_JSIMMEDIATE) + // See comment in op_get_by_val. + zeroExtend32ToPtr(regT1, regT1); +#else + emitFastArithImmToInt(regT1); +#endif + emitJumpSlowCaseIfNotJSCell(regT0); + addSlowCase(branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr))); + + // This is an array; get the m_storage pointer into ecx, then check if the index is below the fast cutoff + loadPtr(Address(regT0, FIELD_OFFSET(JSArray, m_storage)), regT2); + Jump inFastVector = branch32(Below, regT1, Address(regT0, FIELD_OFFSET(JSArray, m_fastAccessCutoff))); + // No; oh well, check if the access if within the vector - if so, we may still be okay. + addSlowCase(branch32(AboveOrEqual, regT1, Address(regT2, FIELD_OFFSET(ArrayStorage, m_vectorLength)))); + + // This is a write to the slow part of the vector; first, we have to check if this would be the first write to this location. + // FIXME: should be able to handle initial write to array; increment the the number of items in the array, and potentially update fast access cutoff. + addSlowCase(branchTestPtr(Zero, BaseIndex(regT2, regT1, ScalePtr, FIELD_OFFSET(ArrayStorage, m_vector[0])))); + + // All good - put the value into the array. + inFastVector.link(this); + emitGetVirtualRegister(currentInstruction[3].u.operand, regT0); + storePtr(regT0, BaseIndex(regT2, regT1, ScalePtr, FIELD_OFFSET(ArrayStorage, m_vector[0]))); +} - emitGetVirtualRegister(baseVReg, X86::eax); +void JIT::emit_op_put_by_index(Instruction* currentInstruction) +{ + JITStubCall stubCall(this, JITStubs::cti_op_put_by_index); + stubCall.addArgument(currentInstruction[1].u.operand, regT2); + stubCall.addArgument(Imm32(currentInstruction[2].u.operand)); + stubCall.addArgument(currentInstruction[3].u.operand, regT2); + stubCall.call(); +} - emitPutJITStubArg(X86::eax, 1); - emitPutJITStubArgConstant(ident, 2); - emitCTICall(Interpreter::cti_op_get_by_id_generic); - emitPutVirtualRegister(resultVReg); +void JIT::emit_op_put_getter(Instruction* currentInstruction) +{ + JITStubCall stubCall(this, JITStubs::cti_op_put_getter); + stubCall.addArgument(currentInstruction[1].u.operand, regT2); + stubCall.addArgument(ImmPtr(&m_codeBlock->identifier(currentInstruction[2].u.operand))); + stubCall.addArgument(currentInstruction[3].u.operand, regT2); + stubCall.call(); } +void JIT::emit_op_put_setter(Instruction* currentInstruction) +{ + JITStubCall stubCall(this, JITStubs::cti_op_put_setter); + stubCall.addArgument(currentInstruction[1].u.operand, regT2); + stubCall.addArgument(ImmPtr(&m_codeBlock->identifier(currentInstruction[2].u.operand))); + stubCall.addArgument(currentInstruction[3].u.operand, regT2); + stubCall.call(); +} -void JIT::compileGetByIdSlowCase(int, int, Identifier*, Vector<SlowCaseEntry>::iterator&, unsigned) +void JIT::emit_op_del_by_id(Instruction* currentInstruction) +{ + JITStubCall stubCall(this, JITStubs::cti_op_del_by_id); + stubCall.addArgument(currentInstruction[2].u.operand, regT2); + stubCall.addArgument(ImmPtr(&m_codeBlock->identifier(currentInstruction[3].u.operand))); + stubCall.call(currentInstruction[1].u.operand); +} + + +#if !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) + +/* ------------------------------ BEGIN: !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) ------------------------------ */ + +// Treat these as nops - the call will be handed as a regular get_by_id/op_call pair. +void JIT::emit_op_method_check(Instruction*) {} +void JIT::emitSlow_op_method_check(Instruction*, Vector<SlowCaseEntry>::iterator&) { ASSERT_NOT_REACHED(); } +#if ENABLE(JIT_OPTIMIZE_METHOD_CALLS) +#error "JIT_OPTIMIZE_METHOD_CALLS requires JIT_OPTIMIZE_PROPERTY_ACCESS" +#endif + +void JIT::emit_op_get_by_id(Instruction* currentInstruction) +{ + unsigned resultVReg = currentInstruction[1].u.operand; + unsigned baseVReg = currentInstruction[2].u.operand; + Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand)); + + emitGetVirtualRegister(baseVReg, regT0); + JITStubCall stubCall(this, JITStubs::cti_op_get_by_id_generic); + stubCall.addArgument(regT0); + stubCall.addArgument(ImmPtr(ident)); + stubCall.call(resultVReg); + + m_propertyAccessInstructionIndex++; +} + +void JIT::emitSlow_op_get_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&) { ASSERT_NOT_REACHED(); } -void JIT::compilePutByIdHotPath(int baseVReg, Identifier* ident, int valueVReg, unsigned) +void JIT::emit_op_put_by_id(Instruction* currentInstruction) { - // 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. + unsigned baseVReg = currentInstruction[1].u.operand; + Identifier* ident = &(m_codeBlock->identifier(currentInstruction[2].u.operand)); + unsigned valueVReg = currentInstruction[3].u.operand; + + emitGetVirtualRegisters(baseVReg, regT0, valueVReg, regT1); - emitGetVirtualRegisters(baseVReg, X86::eax, valueVReg, X86::edx); + JITStubCall stubCall(this, JITStubs::cti_op_put_by_id_generic); + stubCall.addArgument(regT0); + stubCall.addArgument(ImmPtr(ident)); + stubCall.addArgument(regT1); + stubCall.call(); - emitPutJITStubArgConstant(ident, 2); - emitPutJITStubArg(X86::eax, 1); - emitPutJITStubArg(X86::edx, 3); - emitCTICall(Interpreter::cti_op_put_by_id_generic); + m_propertyAccessInstructionIndex++; } -void JIT::compilePutByIdSlowCase(int, Identifier*, int, Vector<SlowCaseEntry>::iterator&, unsigned) +void JIT::emitSlow_op_put_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&) { ASSERT_NOT_REACHED(); } -#else +#else // !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) + +/* ------------------------------ BEGIN: ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) ------------------------------ */ + +#if ENABLE(JIT_OPTIMIZE_METHOD_CALLS) -void JIT::compileGetByIdHotPath(int resultVReg, int baseVReg, Identifier*, unsigned propertyAccessInstructionIndex) +void JIT::emit_op_method_check(Instruction* currentInstruction) +{ + // Assert that the following instruction is a get_by_id. + ASSERT(m_interpreter->getOpcodeID((currentInstruction + OPCODE_LENGTH(op_method_check))->u.opcode) == op_get_by_id); + + currentInstruction += OPCODE_LENGTH(op_method_check); + unsigned resultVReg = currentInstruction[1].u.operand; + unsigned baseVReg = currentInstruction[2].u.operand; + Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand)); + + emitGetVirtualRegister(baseVReg, regT0); + + // Do the method check - check the object & its prototype's structure inline (this is the common case). + m_methodCallCompilationInfo.append(MethodCallCompilationInfo(m_propertyAccessInstructionIndex)); + MethodCallCompilationInfo& info = m_methodCallCompilationInfo.last(); + Jump notCell = emitJumpIfNotJSCell(regT0); + Jump structureCheck = branchPtrWithPatch(NotEqual, Address(regT0, FIELD_OFFSET(JSCell, m_structure)), info.structureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure))); + DataLabelPtr protoStructureToCompare, protoObj = moveWithPatch(ImmPtr(0), regT1); + Jump protoStructureCheck = branchPtrWithPatch(NotEqual, Address(regT1, FIELD_OFFSET(JSCell, m_structure)), protoStructureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure))); + + // This will be relinked to load the function without doing a load. + DataLabelPtr putFunction = moveWithPatch(ImmPtr(0), regT0); + Jump match = jump(); + + ASSERT(differenceBetween(info.structureToCompare, protoObj) == patchOffsetMethodCheckProtoObj); + ASSERT(differenceBetween(info.structureToCompare, protoStructureToCompare) == patchOffsetMethodCheckProtoStruct); + ASSERT(differenceBetween(info.structureToCompare, putFunction) == patchOffsetMethodCheckPutFunction); + + // Link the failure cases here. + notCell.link(this); + structureCheck.link(this); + protoStructureCheck.link(this); + + // Do a regular(ish) get_by_id (the slow case will be link to + // cti_op_get_by_id_method_check instead of cti_op_get_by_id. + compileGetByIdHotPath(resultVReg, baseVReg, ident, m_propertyAccessInstructionIndex++); + + match.link(this); + emitPutVirtualRegister(resultVReg); + + // We've already generated the following get_by_id, so make sure it's skipped over. + m_bytecodeIndex += OPCODE_LENGTH(op_get_by_id); +} + +void JIT::emitSlow_op_method_check(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) +{ + currentInstruction += OPCODE_LENGTH(op_method_check); + unsigned resultVReg = currentInstruction[1].u.operand; + unsigned baseVReg = currentInstruction[2].u.operand; + Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand)); + + compileGetByIdSlowCase(resultVReg, baseVReg, ident, iter, m_propertyAccessInstructionIndex++, true); + + // We've already generated the following get_by_id, so make sure it's skipped over. + m_bytecodeIndex += OPCODE_LENGTH(op_get_by_id); +} + +#else //!ENABLE(JIT_OPTIMIZE_METHOD_CALLS) + +// Treat these as nops - the call will be handed as a regular get_by_id/op_call pair. +void JIT::emit_op_method_check(Instruction*) {} +void JIT::emitSlow_op_method_check(Instruction*, Vector<SlowCaseEntry>::iterator&) { ASSERT_NOT_REACHED(); } + +#endif + +void JIT::emit_op_get_by_id(Instruction* currentInstruction) +{ + unsigned resultVReg = currentInstruction[1].u.operand; + unsigned baseVReg = currentInstruction[2].u.operand; + Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand)); + + emitGetVirtualRegister(baseVReg, regT0); + compileGetByIdHotPath(resultVReg, baseVReg, ident, m_propertyAccessInstructionIndex++); + emitPutVirtualRegister(resultVReg); +} + +void JIT::compileGetByIdHotPath(int, 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); + emitJumpSlowCaseIfNotJSCell(regT0, 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))); + Jump structureCheck = branchPtrWithPatch(NotEqual, Address(regT0, 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); + Label externalLoad(this); + loadPtr(Address(regT0, FIELD_OFFSET(JSObject, m_externalStorage)), regT0); + Label externalLoadComplete(this); + ASSERT(differenceBetween(hotPathBegin, externalLoad) == patchOffsetGetByIdExternalLoad); + ASSERT(differenceBetween(externalLoad, externalLoadComplete) == patchLengthGetByIdExternalLoad); + + DataLabel32 displacementLabel = loadPtrWithAddressOffsetPatch(Address(regT0, patchGetByIdDefaultOffset), regT0); ASSERT(differenceBetween(hotPathBegin, displacementLabel) == patchOffsetGetByIdPropertyMapOffset); Label putResult(this); ASSERT(differenceBetween(hotPathBegin, putResult) == patchOffsetGetByIdPutResult); - emitPutVirtualRegister(resultVReg); } +void JIT::emitSlow_op_get_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) +{ + unsigned resultVReg = currentInstruction[1].u.operand; + unsigned baseVReg = currentInstruction[2].u.operand; + Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand)); -void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident, Vector<SlowCaseEntry>::iterator& iter, unsigned propertyAccessInstructionIndex) + compileGetByIdSlowCase(resultVReg, baseVReg, ident, iter, m_propertyAccessInstructionIndex++, false); +} + +void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident, Vector<SlowCaseEntry>::iterator& iter, unsigned propertyAccessInstructionIndex, bool isMethodCheck) { // 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 @@ -132,10 +326,10 @@ void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident #ifndef NDEBUG Label coldPathBegin(this); #endif - emitPutJITStubArg(X86::eax, 1); - emitPutJITStubArgConstant(ident, 2); - Jump call = emitCTICall(Interpreter::cti_op_get_by_id); - emitPutVirtualRegister(resultVReg); + JITStubCall stubCall(this, isMethodCheck ? JITStubs::cti_op_get_by_id_method_check : JITStubs::cti_op_get_by_id); + stubCall.addArgument(regT0); + stubCall.addArgument(ImmPtr(ident)); + Call call = stubCall.call(resultVReg); ASSERT(differenceBetween(coldPathBegin, call) == patchOffsetGetByIdSlowCaseCall); @@ -143,338 +337,329 @@ void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident m_propertyAccessCompilationInfo[propertyAccessInstructionIndex].callReturnLocation = call; } -void JIT::compilePutByIdHotPath(int baseVReg, Identifier*, int valueVReg, unsigned propertyAccessInstructionIndex) +void JIT::emit_op_put_by_id(Instruction* currentInstruction) { + unsigned baseVReg = currentInstruction[1].u.operand; + unsigned valueVReg = currentInstruction[3].u.operand; + + unsigned propertyAccessInstructionIndex = m_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); + emitGetVirtualRegisters(baseVReg, regT0, valueVReg, regT1); // Jump to a slow case if either the base object is an immediate, or if the Structure does not match. - emitJumpSlowCaseIfNotJSCell(X86::eax, baseVReg); + emitJumpSlowCaseIfNotJSCell(regT0, 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)))); + addSlowCase(branchPtrWithPatch(NotEqual, Address(regT0, 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)); + Label externalLoad(this); + loadPtr(Address(regT0, FIELD_OFFSET(JSObject, m_externalStorage)), regT0); + Label externalLoadComplete(this); + ASSERT(differenceBetween(hotPathBegin, externalLoad) == patchOffsetPutByIdExternalLoad); + ASSERT(differenceBetween(externalLoad, externalLoadComplete) == patchLengthPutByIdExternalLoad); + + DataLabel32 displacementLabel = storePtrWithAddressOffsetPatch(regT1, Address(regT0, patchGetByIdDefaultOffset)); ASSERT(differenceBetween(hotPathBegin, displacementLabel) == patchOffsetPutByIdPropertyMapOffset); } -void JIT::compilePutByIdSlowCase(int baseVReg, Identifier* ident, int, Vector<SlowCaseEntry>::iterator& iter, unsigned propertyAccessInstructionIndex) +void JIT::emitSlow_op_put_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) { + unsigned baseVReg = currentInstruction[1].u.operand; + Identifier* ident = &(m_codeBlock->identifier(currentInstruction[2].u.operand)); + + unsigned propertyAccessInstructionIndex = m_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); + JITStubCall stubCall(this, JITStubs::cti_op_put_by_id); + stubCall.addArgument(regT0); + stubCall.addArgument(ImmPtr(ident)); + stubCall.addArgument(regT1); + Call call = stubCall.call(); // 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) +// Compile a store into an object's property storage. May overwrite the +// value in objectReg. +void JIT::compilePutDirectOffset(RegisterID base, RegisterID value, Structure* structure, size_t cachedOffset) { - baseObject->allocatePropertyStorage(oldSize, newSize); - return baseObject; + int offset = cachedOffset * sizeof(JSValue); + if (structure->isUsingInlineStorage()) + offset += FIELD_OFFSET(JSObject, m_inlineStorage); + else + loadPtr(Address(base, FIELD_OFFSET(JSObject, m_externalStorage)), base); + storePtr(value, Address(base, offset)); } -static inline bool transitionWillNeedStorageRealloc(Structure* oldStructure, Structure* newStructure) +// Compile a load from an object's property storage. May overwrite base. +void JIT::compileGetDirectOffset(RegisterID base, RegisterID result, Structure* structure, size_t cachedOffset) { - return oldStructure->propertyStorageCapacity() != newStructure->propertyStorageCapacity(); + int offset = cachedOffset * sizeof(JSValue); + if (structure->isUsingInlineStorage()) + offset += FIELD_OFFSET(JSObject, m_inlineStorage); + else + loadPtr(Address(base, FIELD_OFFSET(JSObject, m_externalStorage)), base); + loadPtr(Address(base, offset), result); } -void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure* oldStructure, Structure* newStructure, size_t cachedOffset, StructureChain* chain, void* returnAddress) +void JIT::compileGetDirectOffset(JSObject* base, RegisterID result, size_t cachedOffset) +{ + if (base->isUsingInlineStorage()) + loadPtr(static_cast<void*>(&base->m_inlineStorage[cachedOffset]), result); + else + loadPtr(static_cast<void*>(&base->m_externalStorage[cachedOffset]), result); +} + +void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure* oldStructure, Structure* newStructure, size_t cachedOffset, StructureChain* chain, ProcessorReturnAddress 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))); + failureCases.append(emitJumpIfNotJSCell(regT0)); + failureCases.append(branchPtr(NotEqual, Address(regT0, FIELD_OFFSET(JSCell, m_structure)), ImmPtr(oldStructure))); JumpList successCases; - // ecx = baseObject - loadPtr(Address(X86::eax, FIELD_OFFSET(JSCell, m_structure)), X86::ecx); + // ecx = baseObject + loadPtr(Address(regT0, FIELD_OFFSET(JSCell, m_structure)), regT2); // proto(ecx) = baseObject->structure()->prototype() - failureCases.append(jne32(Address(X86::ecx, FIELD_OFFSET(Structure, m_typeInfo) + FIELD_OFFSET(TypeInfo, m_type)), Imm32(ObjectType))); + failureCases.append(branch32(NotEqual, Address(regT2, FIELD_OFFSET(Structure, m_typeInfo) + FIELD_OFFSET(TypeInfo, m_type)), Imm32(ObjectType))); - loadPtr(Address(X86::ecx, FIELD_OFFSET(Structure, m_prototype)), X86::ecx); + loadPtr(Address(regT2, FIELD_OFFSET(Structure, m_prototype)), regT2); // 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())))); + successCases.append(branchPtr(Equal, regT2, ImmPtr(JSValue::encode(jsNull())))); // Check the structure id - failureCases.append(jnePtr(Address(X86::ecx, FIELD_OFFSET(JSCell, m_structure)), ImmPtr(it->get()))); + failureCases.append(branchPtr(NotEqual, Address(regT2, 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); + loadPtr(Address(regT2, FIELD_OFFSET(JSCell, m_structure)), regT2); + failureCases.append(branch32(NotEqual, Address(regT2, FIELD_OFFSET(Structure, m_typeInfo) + FIELD_OFFSET(TypeInfo, m_type)), Imm32(ObjectType))); + loadPtr(Address(regT2, FIELD_OFFSET(Structure, m_prototype)), regT2); } successCases.link(this); - Jump callTarget; + Call 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); + bool willNeedStorageRealloc = oldStructure->propertyStorageCapacity() != newStructure->propertyStorageCapacity(); + 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. + preverveReturnAddressAfterCall(regT3); + + JITStubCall stubCall(this, JITStubs::cti_op_put_by_id_transition_realloc); + stubCall.addArgument(regT0); + stubCall.addArgument(Imm32(oldStructure->propertyStorageCapacity())); + stubCall.addArgument(Imm32(newStructure->propertyStorageCapacity())); + stubCall.addArgument(regT1); // This argument is not used in the stub; we set it up on the stack so that it can be restored, below. + stubCall.call(regT0); + emitGetJITStubArg(4, regT1); + + restoreReturnAddressBeforeReturn(regT3); } // 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))); + storePtr(ImmPtr(newStructure), Address(regT0, 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))); + compilePutDirectOffset(regT0, regT1, newStructure, cachedOffset); ret(); - Jump failureJump; - bool plantedFailureJump = false; - if (!failureCases.empty()) { - failureCases.link(this); - restoreArgumentReferenceForTrampoline(); - failureJump = jump(); - plantedFailureJump = true; - } + ASSERT(!failureCases.empty()); + failureCases.link(this); + restoreArgumentReferenceForTrampoline(); + Call failureCall = tailRecursiveCall(); - void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); - PatchBuffer patchBuffer(code); + PatchBuffer patchBuffer(this, m_codeBlock->executablePool()); - if (plantedFailureJump) - patchBuffer.link(failureJump, reinterpret_cast<void*>(Interpreter::cti_op_put_by_id_fail)); + patchBuffer.link(failureCall, FunctionPtr(JITStubs::cti_op_put_by_id_fail)); - if (transitionWillNeedStorageRealloc(oldStructure, newStructure)) - patchBuffer.link(callTarget, reinterpret_cast<void*>(resizePropertyStorage)); - - stubInfo->stubRoutine = code; + if (willNeedStorageRealloc) { + ASSERT(m_calls.size() == 1); + patchBuffer.link(m_calls[0].from, FunctionPtr(JITStubs::cti_op_put_by_id_transition_realloc)); + } - Jump::patch(returnAddress, code); + CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); + stubInfo->stubRoutine = entryLabel; + returnAddress.relinkCallerToTrampoline(entryLabel); } -void JIT::patchGetByIdSelf(StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, void* returnAddress) +void JIT::patchGetByIdSelf(StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, ProcessorReturnAddress 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)); + // Should probably go to JITStubs::cti_op_get_by_id_fail, but that doesn't do anything interesting right now. + returnAddress.relinkCallerToFunction(FunctionPtr(JITStubs::cti_op_get_by_id_self_fail)); + + int offset = sizeof(JSValue) * cachedOffset; + + // If we're patching to use inline storage, convert the initial load to a lea; this avoids the extra load + // and makes the subsequent load's offset automatically correct + if (structure->isUsingInlineStorage()) + stubInfo->hotPathBegin.instructionAtOffset(patchOffsetGetByIdExternalLoad).repatchLoadPtrToLEA(); // 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)); + stubInfo->hotPathBegin.dataLabelPtrAtOffset(patchOffsetGetByIdStructure).repatch(structure); + stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetGetByIdPropertyMapOffset).repatch(offset); +} + +void JIT::patchMethodCallProto(MethodCallLinkInfo& methodCallLinkInfo, JSFunction* callee, Structure* structure, JSObject* proto) +{ + ASSERT(!methodCallLinkInfo.cachedStructure); + methodCallLinkInfo.cachedStructure = structure; + structure->ref(); + + methodCallLinkInfo.structureLabel.repatch(structure); + methodCallLinkInfo.structureLabel.dataLabelPtrAtOffset(patchOffsetMethodCheckProtoObj).repatch(proto); + methodCallLinkInfo.structureLabel.dataLabelPtrAtOffset(patchOffsetMethodCheckProtoStruct).repatch(proto->structure()); + methodCallLinkInfo.structureLabel.dataLabelPtrAtOffset(patchOffsetMethodCheckPutFunction).repatch(callee); } -void JIT::patchPutByIdReplace(StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, void* returnAddress) +void JIT::patchPutByIdReplace(StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, ProcessorReturnAddress 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)); + // Should probably go to JITStubs::cti_op_put_by_id_fail, but that doesn't do anything interesting right now. + returnAddress.relinkCallerToFunction(FunctionPtr(JITStubs::cti_op_put_by_id_generic)); + + int offset = sizeof(JSValue) * cachedOffset; + + // If we're patching to use inline storage, convert the initial load to a lea; this avoids the extra load + // and makes the subsequent load's offset automatically correct + if (structure->isUsingInlineStorage()) + stubInfo->hotPathBegin.instructionAtOffset(patchOffsetPutByIdExternalLoad).repatchLoadPtrToLEA(); // 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)); + stubInfo->hotPathBegin.dataLabelPtrAtOffset(patchOffsetPutByIdStructure).repatch(structure); + stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetPutByIdPropertyMapOffset).repatch(offset); } -void JIT::privateCompilePatchGetArrayLength(void* returnAddress) +void JIT::privateCompilePatchGetArrayLength(ProcessorReturnAddress returnAddress) { - StructureStubInfo* stubInfo = &m_codeBlock->getStubInfo(returnAddress); + StructureStubInfo* stubInfo = &m_codeBlock->getStubInfo(returnAddress.addressForLookup()); // 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)); + returnAddress.relinkCallerToFunction(FunctionPtr(JITStubs::cti_op_get_by_id_array_fail)); // Check eax is an array - Jump failureCases1 = jnePtr(Address(X86::eax), ImmPtr(m_interpreter->m_jsArrayVptr)); + Jump failureCases1 = branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->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); + loadPtr(Address(regT0, FIELD_OFFSET(JSArray, m_storage)), regT2); + load32(Address(regT2, FIELD_OFFSET(ArrayStorage, m_length)), regT2); - Jump failureCases2 = ja32(X86::ecx, Imm32(JSImmediate::maxImmediateInt)); + Jump failureCases2 = branch32(Above, regT2, Imm32(JSImmediate::maxImmediateInt)); - emitFastArithIntToImmNoCheck(X86::ecx, X86::eax); + emitFastArithIntToImmNoCheck(regT2, regT0); Jump success = jump(); - void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); - PatchBuffer patchBuffer(code); + PatchBuffer patchBuffer(this, m_codeBlock->executablePool()); // Use the patch information to link the failure cases back to the original slow case routine. - void* slowCaseBegin = reinterpret_cast<char*>(stubInfo->callReturnLocation) - patchOffsetGetByIdSlowCaseCall; + CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-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); + patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult)); // Track the stub we have created so that it will be deleted later. - stubInfo->stubRoutine = code; + CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); + stubInfo->stubRoutine = entryLabel; - // 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); + // Finally patch the jump to slow case back in the hot path to jump here instead. + CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase); + jumpLocation.relink(entryLabel); } -void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, void* returnAddress, CallFrame* callFrame) +void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, ProcessorReturnAddress 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)); + returnAddress.relinkCallerToFunction(FunctionPtr(JITStubs::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); + Jump failureCases1 = checkStructure(regT0, 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)); + move(ImmPtr(prototypeStructure), regT3); + Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3); #else - Jump failureCases2 = jnePtr(AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure)); + Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure)); #endif // Checks out okay! - getDirectOffset - loadPtr(Address(X86::edx, cachedOffset * sizeof(JSValuePtr)), X86::eax); + compileGetDirectOffset(protoObject, regT0, cachedOffset); Jump success = jump(); - void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); - PatchBuffer patchBuffer(code); + PatchBuffer patchBuffer(this, m_codeBlock->executablePool()); // Use the patch information to link the failure cases back to the original slow case routine. - void* slowCaseBegin = reinterpret_cast<char*>(stubInfo->callReturnLocation) - patchOffsetGetByIdSlowCaseCall; + CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-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)); + patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult)); // Track the stub we have created so that it will be deleted later. - stubInfo->stubRoutine = code; + CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); + stubInfo->stubRoutine = entryLabel; // 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 + CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase); + jumpLocation.relink(entryLabel); } -#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 failureCase = checkStructure(regT0, structure); + compileGetDirectOffset(regT0, regT0, structure, cachedOffset); Jump success = jump(); - void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); - ASSERT(code); - PatchBuffer patchBuffer(code); + PatchBuffer patchBuffer(this, m_codeBlock->executablePool()); // Use the patch information to link the failure cases back to the original slow case routine. - void* lastProtoBegin = polymorphicStructures->list[currentIndex - 1].stubRoutine; + CodeLocationLabel lastProtoBegin = polymorphicStructures->list[currentIndex - 1].stubRoutine; if (!lastProtoBegin) - lastProtoBegin = reinterpret_cast<char*>(stubInfo->callReturnLocation) - patchOffsetGetByIdSlowCaseCall; + lastProtoBegin = stubInfo->callReturnLocation.labelAtOffset(-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)); + patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult)); + + CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); structure->ref(); - polymorphicStructures->list[currentIndex].set(code, structure); + polymorphicStructures->list[currentIndex].set(entryLabel, 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); + CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase); + jumpLocation.relink(entryLabel); } void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, CallFrame* callFrame) @@ -482,45 +667,43 @@ void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, Polymorphi // 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); + Jump failureCases1 = checkStructure(regT0, 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)); + move(ImmPtr(prototypeStructure), regT3); + Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3); #else - Jump failureCases2 = jnePtr(AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure)); + Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure)); #endif // Checks out okay! - getDirectOffset - loadPtr(Address(X86::edx, cachedOffset * sizeof(JSValuePtr)), X86::eax); + compileGetDirectOffset(protoObject, regT0, cachedOffset); Jump success = jump(); - void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); - PatchBuffer patchBuffer(code); + PatchBuffer patchBuffer(this, m_codeBlock->executablePool()); // Use the patch information to link the failure cases back to the original slow case routine. - void* lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine; + CodeLocationLabel 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)); + patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult)); + + CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); structure->ref(); prototypeStructure->ref(); - prototypeStructures->list[currentIndex].set(code, structure, prototypeStructure); + prototypeStructures->list[currentIndex].set(entryLabel, 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); + CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase); + jumpLocation.relink(entryLabel); } void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, CallFrame* callFrame) @@ -530,7 +713,7 @@ void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, Polymorphi JumpList bucketsOfFail; // Check eax is an object of the right Structure. - Jump baseObjectCheck = checkStructure(X86::eax, structure); + Jump baseObjectCheck = checkStructure(regT0, structure); bucketsOfFail.append(baseObjectCheck); Structure* currStructure = structure; @@ -543,54 +726,50 @@ void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, Polymorphi // 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))); + move(ImmPtr(currStructure), regT3); + bucketsOfFail.append(branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3)); #else - bucketsOfFail.append(jnePtr(AbsoluteAddress(prototypeStructureAddress), ImmPtr(currStructure))); + bucketsOfFail.append(branchPtr(NotEqual, 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); + compileGetDirectOffset(protoObject, regT0, cachedOffset); Jump success = jump(); - void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); - PatchBuffer patchBuffer(code); + PatchBuffer patchBuffer(this, m_codeBlock->executablePool()); // Use the patch information to link the failure cases back to the original slow case routine. - void* lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine; + CodeLocationLabel 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)); + patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult)); + + CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); // Track the stub we have created so that it will be deleted later. structure->ref(); chain->ref(); - prototypeStructures->list[currentIndex].set(code, structure, chain); + prototypeStructures->list[currentIndex].set(entryLabel, 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); + CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase); + jumpLocation.relink(entryLabel); } -#endif -void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, void* returnAddress, CallFrame* callFrame) +void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, ProcessorReturnAddress 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)); + returnAddress.relinkCallerToFunction(FunctionPtr(JITStubs::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)); + bucketsOfFail.append(checkStructure(regT0, structure)); Structure* currStructure = structure; RefPtr<Structure>* chainEntries = chain->head(); @@ -602,102 +781,37 @@ void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* str // 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))); + move(ImmPtr(currStructure), regT3); + bucketsOfFail.append(branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3)); #else - bucketsOfFail.append(jnePtr(AbsoluteAddress(prototypeStructureAddress), ImmPtr(currStructure))); + bucketsOfFail.append(branchPtr(NotEqual, 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); + compileGetDirectOffset(protoObject, regT0, cachedOffset); Jump success = jump(); - void* code = m_assembler.executableCopy(m_codeBlock->executablePool()); - PatchBuffer patchBuffer(code); + PatchBuffer patchBuffer(this, m_codeBlock->executablePool()); // 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); + patchBuffer.link(bucketsOfFail, stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall)); // 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)); + patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult)); // Track the stub we have created so that it will be deleted later. - stubInfo->stubRoutine = code; + CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); + stubInfo->stubRoutine = entryLabel; // 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 + CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase); + jumpLocation.relink(entryLabel); } -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)); +/* ------------------------------ END: !ENABLE / ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) ------------------------------ */ - stubInfo->stubRoutine = code; - - Jump::patch(returnAddress, code); -} - -#endif +#endif // !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) } // namespace JSC |