X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/1df5f87f1309a8daa30dabdee855f48ae40d14ab..6fe7ccc865dc7d7541b93c5bcaf6368d2c98a174:/jit/JITPropertyAccess.cpp diff --git a/jit/JITPropertyAccess.cpp b/jit/JITPropertyAccess.cpp index ce3d3c1..8c7148c 100644 --- a/jit/JITPropertyAccess.cpp +++ b/jit/JITPropertyAccess.cpp @@ -50,24 +50,33 @@ using namespace std; namespace JSC { #if USE(JSVALUE64) -JIT::CodePtr JIT::stringGetByValStubGenerator(JSGlobalData* globalData, ExecutablePool* pool) +JIT::CodeRef JIT::stringGetByValStubGenerator(JSGlobalData* globalData) { JSInterfaceJIT jit; JumpList failures; - failures.append(jit.branchPtr(NotEqual, Address(regT0), TrustedImmPtr(globalData->jsStringVPtr))); - failures.append(jit.branchTest32(NonZero, Address(regT0, OBJECT_OFFSETOF(JSString, m_fiberCount)))); + failures.append(jit.branchPtr(NotEqual, Address(regT0, JSCell::classInfoOffset()), TrustedImmPtr(&JSString::s_info))); // Load string length to regT2, and start the process of loading the data pointer into regT0 jit.load32(Address(regT0, ThunkHelpers::jsStringLengthOffset()), regT2); jit.loadPtr(Address(regT0, ThunkHelpers::jsStringValueOffset()), regT0); - jit.loadPtr(Address(regT0, ThunkHelpers::stringImplDataOffset()), regT0); - + failures.append(jit.branchTest32(Zero, regT0)); + // Do an unsigned compare to simultaneously filter negative indices as well as indices that are too large failures.append(jit.branch32(AboveOrEqual, regT1, regT2)); // Load the character + JumpList is16Bit; + JumpList cont8Bit; + // Load the string flags + jit.loadPtr(Address(regT0, ThunkHelpers::stringImplFlagsOffset()), regT2); + jit.loadPtr(Address(regT0, ThunkHelpers::stringImplDataOffset()), regT0); + is16Bit.append(jit.branchTest32(Zero, regT2, TrustedImm32(ThunkHelpers::stringImpl8BitFlag()))); + jit.load8(BaseIndex(regT0, regT1, TimesOne, 0), regT0); + cont8Bit.append(jit.jump()); + is16Bit.link(&jit); jit.load16(BaseIndex(regT0, regT1, TimesTwo, 0), regT0); - + cont8Bit.link(&jit); + failures.append(jit.branch32(AboveOrEqual, regT0, TrustedImm32(0x100))); jit.move(TrustedImmPtr(globalData->smallStrings.singleCharacterStrings()), regT1); jit.loadPtr(BaseIndex(regT1, regT0, ScalePtr, 0), regT0); @@ -77,8 +86,8 @@ JIT::CodePtr JIT::stringGetByValStubGenerator(JSGlobalData* globalData, Executab jit.move(TrustedImm32(0), regT0); jit.ret(); - LinkBuffer patchBuffer(*globalData, &jit, pool); - return patchBuffer.finalizeCode().m_code; + LinkBuffer patchBuffer(*globalData, &jit, GLOBAL_THUNK_ID); + return patchBuffer.finalizeCode(); } void JIT::emit_op_get_by_val(Instruction* currentInstruction) @@ -99,7 +108,7 @@ void JIT::emit_op_get_by_val(Instruction* currentInstruction) zeroExtend32ToPtr(regT1, regT1); emitJumpSlowCaseIfNotJSCell(regT0, base); - addSlowCase(branchPtr(NotEqual, Address(regT0), TrustedImmPtr(m_globalData->jsArrayVPtr))); + addSlowCase(branchPtr(NotEqual, Address(regT0, JSCell::classInfoOffset()), TrustedImmPtr(&JSArray::s_info))); loadPtr(Address(regT0, JSArray::storageOffset()), regT2); addSlowCase(branch32(AboveOrEqual, regT1, Address(regT0, JSArray::vectorLengthOffset()))); @@ -107,6 +116,7 @@ void JIT::emit_op_get_by_val(Instruction* currentInstruction) loadPtr(BaseIndex(regT2, regT1, ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), regT0); addSlowCase(branchTestPtr(Zero, regT0)); + emitValueProfilingSite(); emitPutVirtualRegister(dst); } @@ -120,8 +130,8 @@ void JIT::emitSlow_op_get_by_val(Instruction* currentInstruction, VectorjsStringVPtr)); - emitNakedCall(m_globalData->getCTIStub(stringGetByValStubGenerator)); + Jump notString = branchPtr(NotEqual, Address(regT0, JSCell::classInfoOffset()), TrustedImmPtr(&JSString::s_info)); + emitNakedCall(CodeLocationLabel(m_globalData->getCTIStub(stringGetByValStubGenerator).code())); Jump failed = branchTestPtr(Zero, regT0); emitPutVirtualRegister(dst, regT0); emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_get_by_val)); @@ -136,11 +146,13 @@ void JIT::emitSlow_op_get_by_val(Instruction* currentInstruction, VectorjsArrayVPtr))); + addSlowCase(branchPtr(NotEqual, Address(regT0, JSCell::classInfoOffset()), TrustedImmPtr(&JSArray::s_info))); addSlowCase(branch32(AboveOrEqual, regT1, Address(regT0, JSArray::vectorLengthOffset()))); loadPtr(Address(regT0, JSArray::storageOffset()), regT2); Jump empty = branchTestPtr(Zero, BaseIndex(regT2, regT1, ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); Label storeResult(this); - emitGetVirtualRegister(value, regT0); - storePtr(regT0, BaseIndex(regT2, regT1, ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); + emitGetVirtualRegister(value, regT3); + storePtr(regT3, BaseIndex(regT2, regT1, ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); Jump end = jump(); empty.link(this); add32(TrustedImm32(1), Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); branch32(Below, regT1, Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length))).linkTo(storeResult, this); - move(regT1, regT0); - add32(TrustedImm32(1), regT0); - store32(regT0, Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length))); + add32(TrustedImm32(1), regT1); + store32(regT1, Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length))); + sub32(TrustedImm32(1), regT1); jump().linkTo(storeResult, this); end.link(this); + + emitWriteBarrier(regT0, regT3, regT1, regT3, ShouldFilterImmediates, WriteBarrierForPropertyAccess); } -void JIT::emit_op_put_by_index(Instruction* currentInstruction) +void JIT::emitSlow_op_put_by_val(Instruction* currentInstruction, Vector::iterator& iter) { - JITStubCall stubCall(this, cti_op_put_by_index); - stubCall.addArgument(currentInstruction[1].u.operand, regT2); - stubCall.addArgument(TrustedImm32(currentInstruction[2].u.operand)); - stubCall.addArgument(currentInstruction[3].u.operand, regT2); - stubCall.call(); + unsigned base = currentInstruction[1].u.operand; + unsigned property = currentInstruction[2].u.operand; + unsigned value = currentInstruction[3].u.operand; + + linkSlowCase(iter); // property int32 check + linkSlowCaseIfNotJSCell(iter, base); // base cell check + linkSlowCase(iter); // base not array check + linkSlowCase(iter); // in vector check + + JITStubCall stubPutByValCall(this, cti_op_put_by_val); + stubPutByValCall.addArgument(regT0); + stubPutByValCall.addArgument(property, regT2); + stubPutByValCall.addArgument(value, regT2); + stubPutByValCall.call(); } -void JIT::emit_op_put_getter(Instruction* currentInstruction) +void JIT::emit_op_put_by_index(Instruction* currentInstruction) { - JITStubCall stubCall(this, cti_op_put_getter); + JITStubCall stubCall(this, cti_op_put_by_index); stubCall.addArgument(currentInstruction[1].u.operand, regT2); - stubCall.addArgument(TrustedImmPtr(&m_codeBlock->identifier(currentInstruction[2].u.operand))); + stubCall.addArgument(TrustedImm32(currentInstruction[2].u.operand)); stubCall.addArgument(currentInstruction[3].u.operand, regT2); stubCall.call(); } -void JIT::emit_op_put_setter(Instruction* currentInstruction) +void JIT::emit_op_put_getter_setter(Instruction* currentInstruction) { - JITStubCall stubCall(this, cti_op_put_setter); + JITStubCall stubCall(this, cti_op_put_getter_setter); stubCall.addArgument(currentInstruction[1].u.operand, regT2); stubCall.addArgument(TrustedImmPtr(&m_codeBlock->identifier(currentInstruction[2].u.operand))); stubCall.addArgument(currentInstruction[3].u.operand, regT2); + stubCall.addArgument(currentInstruction[4].u.operand, regT2); stubCall.call(); } @@ -255,67 +279,6 @@ void JIT::emit_op_del_by_id(Instruction* currentInstruction) 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::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, cti_op_get_by_id_generic); - stubCall.addArgument(regT0); - stubCall.addArgument(TrustedImmPtr(ident)); - stubCall.call(resultVReg); - - m_propertyAccessInstructionIndex++; -} - -void JIT::emitSlow_op_get_by_id(Instruction*, Vector::iterator&) -{ - ASSERT_NOT_REACHED(); -} - -void JIT::emit_op_put_by_id(Instruction* currentInstruction) -{ - unsigned baseVReg = currentInstruction[1].u.operand; - Identifier* ident = &(m_codeBlock->identifier(currentInstruction[2].u.operand)); - unsigned valueVReg = currentInstruction[3].u.operand; - unsigned direct = currentInstruction[8].u.operand; - - emitGetVirtualRegisters(baseVReg, regT0, valueVReg, regT1); - - JITStubCall stubCall(this, direct ? cti_op_put_by_id_direct_generic, cti_op_put_by_id_generic); - stubCall.addArgument(regT0); - stubCall.addArgument(TrustedImmPtr(ident)); - stubCall.addArgument(regT1); - stubCall.call(); - - m_propertyAccessInstructionIndex++; -} - -void JIT::emitSlow_op_put_by_id(Instruction*, Vector::iterator&) -{ - ASSERT_NOT_REACHED(); -} - -#else // !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) - -/* ------------------------------ BEGIN: ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) ------------------------------ */ - -#if ENABLE(JIT_OPTIMIZE_METHOD_CALLS) - void JIT::emit_op_method_check(Instruction* currentInstruction) { // Assert that the following instruction is a get_by_id. @@ -329,7 +292,7 @@ void JIT::emit_op_method_check(Instruction* currentInstruction) 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)); + m_methodCallCompilationInfo.append(MethodCallCompilationInfo(m_bytecodeOffset, m_propertyAccessCompilationInfo.size())); MethodCallCompilationInfo& info = m_methodCallCompilationInfo.last(); Jump notCell = emitJumpIfNotJSCell(regT0); @@ -347,10 +310,6 @@ void JIT::emit_op_method_check(Instruction* currentInstruction) Jump match = jump(); - ASSERT_JIT_OFFSET_UNUSED(protoObj, differenceBetween(info.structureToCompare, protoObj), patchOffsetMethodCheckProtoObj); - ASSERT_JIT_OFFSET(differenceBetween(info.structureToCompare, protoStructureToCompare), patchOffsetMethodCheckProtoStruct); - ASSERT_JIT_OFFSET_UNUSED(putFunction, differenceBetween(info.structureToCompare, putFunction), patchOffsetMethodCheckPutFunction); - // Link the failure cases here. notCell.link(this); structureCheck.link(this); @@ -358,13 +317,16 @@ void JIT::emit_op_method_check(Instruction* currentInstruction) // 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++); + compileGetByIdHotPath(baseVReg, ident); match.link(this); + emitValueProfilingSite(m_bytecodeOffset + OPCODE_LENGTH(op_method_check)); emitPutVirtualRegister(resultVReg); // We've already generated the following get_by_id, so make sure it's skipped over. m_bytecodeOffset += OPCODE_LENGTH(op_get_by_id); + + m_propertyAccessCompilationInfo.last().addMethodCheckInfo(info.structureToCompare, protoObj, protoStructureToCompare, putFunction); } void JIT::emitSlow_op_method_check(Instruction* currentInstruction, Vector::iterator& iter) @@ -375,19 +337,12 @@ void JIT::emitSlow_op_method_check(Instruction* currentInstruction, Vectoridentifier(currentInstruction[3].u.operand)); compileGetByIdSlowCase(resultVReg, baseVReg, ident, iter, true); + emitValueProfilingSite(m_bytecodeOffset + OPCODE_LENGTH(op_method_check)); // We've already generated the following get_by_id, so make sure it's skipped over. m_bytecodeOffset += 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::iterator&) { ASSERT_NOT_REACHED(); } - -#endif - void JIT::emit_op_get_by_id(Instruction* currentInstruction) { unsigned resultVReg = currentInstruction[1].u.operand; @@ -395,11 +350,12 @@ void JIT::emit_op_get_by_id(Instruction* currentInstruction) Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand)); emitGetVirtualRegister(baseVReg, regT0); - compileGetByIdHotPath(resultVReg, baseVReg, ident, m_propertyAccessInstructionIndex++); + compileGetByIdHotPath(baseVReg, ident); + emitValueProfilingSite(); emitPutVirtualRegister(resultVReg); } -void JIT::compileGetByIdHotPath(int, int baseVReg, Identifier*, unsigned propertyAccessInstructionIndex) +void JIT::compileGetByIdHotPath(int baseVReg, Identifier*) { // 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 @@ -411,23 +367,19 @@ void JIT::compileGetByIdHotPath(int, int baseVReg, Identifier*, unsigned propert BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath); Label hotPathBegin(this); - m_propertyAccessCompilationInfo[propertyAccessInstructionIndex].hotPathBegin = hotPathBegin; DataLabelPtr structureToCompare; - Jump structureCheck = branchPtrWithPatch(NotEqual, Address(regT0, JSCell::structureOffset()), structureToCompare, TrustedImmPtr(reinterpret_cast(patchGetByIdDefaultStructure))); + PatchableJump structureCheck = patchableBranchPtrWithPatch(NotEqual, Address(regT0, JSCell::structureOffset()), structureToCompare, TrustedImmPtr(reinterpret_cast(patchGetByIdDefaultStructure))); addSlowCase(structureCheck); - ASSERT_JIT_OFFSET(differenceBetween(hotPathBegin, structureToCompare), patchOffsetGetByIdStructure); - ASSERT_JIT_OFFSET(differenceBetween(hotPathBegin, structureCheck), patchOffsetGetByIdBranchToSlowCase) - loadPtr(Address(regT0, OBJECT_OFFSETOF(JSObject, m_propertyStorage)), regT0); + loadPtr(Address(regT0, JSObject::offsetOfPropertyStorage()), regT0); DataLabelCompact displacementLabel = loadPtrWithCompactAddressOffsetPatch(Address(regT0, patchGetByIdDefaultOffset), regT0); - ASSERT_JIT_OFFSET_UNUSED(displacementLabel, differenceBetween(hotPathBegin, displacementLabel), patchOffsetGetByIdPropertyMapOffset); Label putResult(this); END_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath); - ASSERT_JIT_OFFSET(differenceBetween(hotPathBegin, putResult), patchOffsetGetByIdPutResult); + m_propertyAccessCompilationInfo.append(PropertyStubCompilationInfo(PropertyStubGetById, m_bytecodeOffset, hotPathBegin, structureToCompare, structureCheck, displacementLabel, putResult)); } void JIT::emitSlow_op_get_by_id(Instruction* currentInstruction, Vector::iterator& iter) @@ -437,6 +389,7 @@ void JIT::emitSlow_op_get_by_id(Instruction* currentInstruction, Vectoridentifier(currentInstruction[3].u.operand)); compileGetByIdSlowCase(resultVReg, baseVReg, ident, iter, false); + emitValueProfilingSite(); } void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident, Vector::iterator& iter, bool isMethodCheck) @@ -452,9 +405,7 @@ void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdSlowCase); -#ifndef NDEBUG Label coldPathBegin(this); -#endif JITStubCall stubCall(this, isMethodCheck ? cti_op_get_by_id_method_check : cti_op_get_by_id); stubCall.addArgument(regT0); stubCall.addArgument(TrustedImmPtr(ident)); @@ -462,11 +413,8 @@ void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident END_UNINTERRUPTED_SEQUENCE(sequenceGetByIdSlowCase); - ASSERT_JIT_OFFSET(differenceBetween(coldPathBegin, call), patchOffsetGetByIdSlowCaseCall); - // Track the location of the call; this will be used to recover patch information. - m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex].callReturnLocation = call; - m_propertyAccessInstructionIndex++; + m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex++].slowCaseInfo(PropertyStubGetById, coldPathBegin, call); } void JIT::emit_op_put_by_id(Instruction* currentInstruction) @@ -474,8 +422,6 @@ 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. @@ -488,19 +434,19 @@ void JIT::emit_op_put_by_id(Instruction* currentInstruction) BEGIN_UNINTERRUPTED_SEQUENCE(sequencePutById); 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(branchPtrWithPatch(NotEqual, Address(regT0, JSCell::structureOffset()), structureToCompare, TrustedImmPtr(reinterpret_cast(patchGetByIdDefaultStructure)))); - ASSERT_JIT_OFFSET(differenceBetween(hotPathBegin, structureToCompare), patchOffsetPutByIdStructure); - loadPtr(Address(regT0, OBJECT_OFFSETOF(JSObject, m_propertyStorage)), regT0); - DataLabel32 displacementLabel = storePtrWithAddressOffsetPatch(regT1, Address(regT0, patchPutByIdDefaultOffset)); + loadPtr(Address(regT0, JSObject::offsetOfPropertyStorage()), regT2); + DataLabel32 displacementLabel = storePtrWithAddressOffsetPatch(regT1, Address(regT2, patchPutByIdDefaultOffset)); END_UNINTERRUPTED_SEQUENCE(sequencePutById); - ASSERT_JIT_OFFSET_UNUSED(displacementLabel, differenceBetween(hotPathBegin, displacementLabel), patchOffsetPutByIdPropertyMapOffset); + emitWriteBarrier(regT0, regT1, regT2, regT3, ShouldFilterImmediates, WriteBarrierForPropertyAccess); + + m_propertyAccessCompilationInfo.append(PropertyStubCompilationInfo(PropertyStubPutById, m_bytecodeOffset, hotPathBegin, structureToCompare, displacementLabel)); } void JIT::emitSlow_op_put_by_id(Instruction* currentInstruction, Vector::iterator& iter) @@ -509,8 +455,6 @@ void JIT::emitSlow_op_put_by_id(Instruction* currentInstruction, Vectoridentifier(currentInstruction[2].u.operand)); unsigned direct = currentInstruction[8].u.operand; - unsigned propertyAccessInstructionIndex = m_propertyAccessInstructionIndex++; - linkSlowCaseIfNotJSCell(iter, baseVReg); linkSlowCase(iter); @@ -521,37 +465,30 @@ void JIT::emitSlow_op_put_by_id(Instruction* currentInstruction, VectorisUsingInlineStorage()) - offset += JSObject::offsetOfInlineStorage(); - else - loadPtr(Address(base, OBJECT_OFFSETOF(JSObject, m_propertyStorage)), base); + loadPtr(Address(base, JSObject::offsetOfPropertyStorage()), base); storePtr(value, Address(base, offset)); } // Compile a load from an object's property storage. May overwrite base. -void JIT::compileGetDirectOffset(RegisterID base, RegisterID result, Structure* structure, size_t cachedOffset) +void JIT::compileGetDirectOffset(RegisterID base, RegisterID result, size_t cachedOffset) { int offset = cachedOffset * sizeof(JSValue); - if (structure->isUsingInlineStorage()) { - offset += JSObject::offsetOfInlineStorage(); - loadPtr(Address(base, offset), result); - } else { - loadPtr(Address(base, OBJECT_OFFSETOF(JSObject, m_propertyStorage)), result); - loadPtr(Address(result, offset), result); - } + loadPtr(Address(base, JSObject::offsetOfPropertyStorage()), result); + loadPtr(Address(result, offset), result); } void JIT::compileGetDirectOffset(JSObject* base, RegisterID result, size_t cachedOffset) { - loadPtr(static_cast(&base->m_propertyStorage[cachedOffset]), result); + loadPtr(base->addressOfPropertyStorage(), result); + loadPtr(Address(result, cachedOffset * sizeof(WriteBarrier)), result); } void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure* oldStructure, Structure* newStructure, size_t cachedOffset, StructureChain* chain, ReturnAddressPtr returnAddress, bool direct) @@ -560,16 +497,29 @@ void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure // Check eax is an object of the right Structure. failureCases.append(emitJumpIfNotJSCell(regT0)); failureCases.append(branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), TrustedImmPtr(oldStructure))); + testPrototype(oldStructure->storedPrototype(), failureCases); + + ASSERT(oldStructure->storedPrototype().isNull() || oldStructure->storedPrototype().asCell()->structure() == chain->head()->get()); // ecx = baseObject->m_structure if (!direct) { - for (WriteBarrier* it = chain->head(); *it; ++it) + for (WriteBarrier* it = chain->head(); *it; ++it) { + ASSERT((*it)->storedPrototype().isNull() || (*it)->storedPrototype().asCell()->structure() == it[1].get()); testPrototype((*it)->storedPrototype(), failureCases); + } } - Call callTarget; - + // 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()) { + sub32( + TrustedImm32(1), + AbsoluteAddress(&m_codeBlock->rareCaseProfileForBytecodeOffset(stubInfo->bytecodeIndex)->m_counter)); + } +#endif + // emit a call only if storage realloc is needed bool willNeedStorageRealloc = oldStructure->propertyStorageCapacity() != newStructure->propertyStorageCapacity(); if (willNeedStorageRealloc) { @@ -582,17 +532,20 @@ void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure stubCall.skipArgument(); // ident stubCall.skipArgument(); // value stubCall.addArgument(TrustedImm32(oldStructure->propertyStorageCapacity())); - stubCall.addArgument(TrustedImm32(newStructure->propertyStorageCapacity())); + stubCall.addArgument(TrustedImmPtr(newStructure)); stubCall.call(regT0); emitGetJITStubArg(2, regT1); restoreReturnAddressBeforeReturn(regT3); } - storePtrWithWriteBarrier(TrustedImmPtr(newStructure), regT0, Address(regT0, JSCell::structureOffset())); + // Planting the new structure triggers the write barrier so we need + // an unconditional barrier here. + emitWriteBarrier(regT0, regT1, regT2, regT3, UnconditionalWriteBarrier, WriteBarrierForPropertyAccess); - // write the value - compilePutDirectOffset(regT0, regT1, newStructure, cachedOffset); + ASSERT(newStructure->classInfo() == oldStructure->classInfo()); + storePtr(TrustedImmPtr(newStructure), Address(regT0, JSCell::structureOffset())); + compilePutDirectOffset(regT0, regT1, cachedOffset); ret(); @@ -601,7 +554,7 @@ void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure restoreArgumentReferenceForTrampoline(); Call failureCall = tailRecursiveCall(); - LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock->executablePool()); + LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock); patchBuffer.link(failureCall, FunctionPtr(direct ? cti_op_put_by_id_direct_fail : cti_op_put_by_id_fail)); @@ -610,10 +563,9 @@ void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure patchBuffer.link(m_calls[0].from, FunctionPtr(cti_op_put_by_id_transition_realloc)); } - CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); - stubInfo->stubRoutine = entryLabel; + stubInfo->stubRoutine = patchBuffer.finalizeCode(); RepatchBuffer repatchBuffer(m_codeBlock); - repatchBuffer.relinkCallerToTrampoline(returnAddress, entryLabel); + repatchBuffer.relinkCallerToTrampoline(returnAddress, CodeLocationLabel(stubInfo->stubRoutine.code())); } void JIT::patchGetByIdSelf(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, ReturnAddressPtr returnAddress) @@ -627,8 +579,8 @@ void JIT::patchGetByIdSelf(CodeBlock* codeBlock, StructureStubInfo* stubInfo, St int offset = sizeof(JSValue) * cachedOffset; // Patch the offset into the propoerty map to load from, then patch the Structure to look for. - repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(patchOffsetGetByIdStructure), structure); - repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelCompactAtOffset(patchOffsetGetByIdPropertyMapOffset), offset); + repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.get.structureToCompare), structure); + repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelCompactAtOffset(stubInfo->patch.baseline.u.get.displacementLabel), offset); } void JIT::patchPutByIdReplace(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, ReturnAddressPtr returnAddress, bool direct) @@ -642,8 +594,8 @@ void JIT::patchPutByIdReplace(CodeBlock* codeBlock, StructureStubInfo* stubInfo, int offset = sizeof(JSValue) * cachedOffset; // Patch the offset into the propoerty map to load from, then patch the Structure to look for. - repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(patchOffsetPutByIdStructure), structure); - repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetPutByIdPropertyMapOffset), offset); + repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.put.structureToCompare), structure); + repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(stubInfo->patch.baseline.u.put.displacementLabel), offset); } void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress) @@ -651,7 +603,7 @@ void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress) StructureStubInfo* stubInfo = &m_codeBlock->getStubInfo(returnAddress); // Check eax is an array - Jump failureCases1 = branchPtr(NotEqual, Address(regT0), TrustedImmPtr(m_globalData->jsArrayVPtr)); + Jump failureCases1 = branchPtr(NotEqual, Address(regT0, JSCell::classInfoOffset()), TrustedImmPtr(&JSArray::s_info)); // Checks out okay! - get the length from the storage loadPtr(Address(regT0, JSArray::storageOffset()), regT3); @@ -661,24 +613,23 @@ void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress) emitFastArithIntToImmNoCheck(regT2, regT0); Jump success = jump(); - LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock->executablePool()); + LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock); // Use the patch information to link the failure cases back to the original slow case routine. - CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall); + CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin); 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. - patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult)); + 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. - CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); - stubInfo->stubRoutine = entryLabel; + stubInfo->stubRoutine = patchBuffer.finalizeCode(); // Finally patch the jump to slow case back in the hot path to jump here instead. - CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase); + CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck); RepatchBuffer repatchBuffer(m_codeBlock); - repatchBuffer.relink(jumpLocation, entryLabel); + repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubInfo->stubRoutine.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)); @@ -719,15 +670,15 @@ void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* str } else compileGetDirectOffset(protoObject, regT0, cachedOffset); Jump success = jump(); - LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock->executablePool()); + LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock); // Use the patch information to link the failure cases back to the original slow case routine. - CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall); + CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin); 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. - patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult)); + patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult)); if (needsStubLink) { for (Vector::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) { @@ -736,13 +687,12 @@ void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* str } } // Track the stub we have created so that it will be deleted later. - CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); - stubInfo->stubRoutine = entryLabel; + stubInfo->stubRoutine = patchBuffer.finalizeCode(); // Finally patch the jump to slow case back in the hot path to jump here instead. - CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase); + CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck); RepatchBuffer repatchBuffer(m_codeBlock); - repatchBuffer.relink(jumpLocation, entryLabel); + repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubInfo->stubRoutine.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)); @@ -752,9 +702,10 @@ void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, Polymorphic { Jump failureCase = checkStructure(regT0, structure); bool needsStubLink = false; + bool isDirect = false; if (slot.cachedPropertyType() == PropertySlot::Getter) { needsStubLink = true; - compileGetDirectOffset(regT0, regT1, structure, cachedOffset); + compileGetDirectOffset(regT0, regT1, cachedOffset); JITStubCall stubCall(this, cti_op_get_by_id_getter_stub); stubCall.addArgument(regT1); stubCall.addArgument(regT0); @@ -768,11 +719,13 @@ void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, Polymorphic stubCall.addArgument(TrustedImmPtr(const_cast(&ident))); stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); stubCall.call(); - } else - compileGetDirectOffset(regT0, regT0, structure, cachedOffset); + } else { + isDirect = true; + compileGetDirectOffset(regT0, regT0, cachedOffset); + } Jump success = jump(); - LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock->executablePool()); + LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock); if (needsStubLink) { for (Vector::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) { @@ -782,23 +735,23 @@ void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, Polymorphic } // Use the patch information to link the failure cases back to the original slow case routine. - CodeLocationLabel lastProtoBegin = polymorphicStructures->list[currentIndex - 1].stubRoutine; + CodeLocationLabel lastProtoBegin = CodeLocationLabel(polymorphicStructures->list[currentIndex - 1].stubRoutine.code()); if (!lastProtoBegin) - lastProtoBegin = stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall); + 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(patchOffsetGetByIdPutResult)); + patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult)); - CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); + MacroAssemblerCodeRef stubCode = patchBuffer.finalizeCode(); - polymorphicStructures->list[currentIndex].set(*m_globalData, m_codeBlock->ownerExecutable(), entryLabel, structure); + polymorphicStructures->list[currentIndex].set(*m_globalData, m_codeBlock->ownerExecutable(), stubCode, structure, isDirect); // Finally patch the jump to slow case back in the hot path to jump here instead. - CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase); + CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck); RepatchBuffer repatchBuffer(m_codeBlock); - repatchBuffer.relink(jumpLocation, entryLabel); + repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubCode.code())); } void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, const Identifier& ident, const PropertySlot& slot, size_t cachedOffset, CallFrame* callFrame) @@ -816,6 +769,7 @@ void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, Polymorphi // Checks out okay! bool needsStubLink = false; + bool isDirect = false; if (slot.cachedPropertyType() == PropertySlot::Getter) { needsStubLink = true; compileGetDirectOffset(protoObject, regT1, cachedOffset); @@ -832,12 +786,14 @@ void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, Polymorphi stubCall.addArgument(TrustedImmPtr(const_cast(&ident))); stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); stubCall.call(); - } else + } else { + isDirect = true; compileGetDirectOffset(protoObject, regT0, cachedOffset); + } Jump success = jump(); - LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock->executablePool()); + LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock); if (needsStubLink) { for (Vector::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) { @@ -847,20 +803,20 @@ void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, Polymorphi } // Use the patch information to link the failure cases back to the original slow case routine. - CodeLocationLabel lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine; + CodeLocationLabel lastProtoBegin = CodeLocationLabel(prototypeStructures->list[currentIndex - 1].stubRoutine.code()); 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. - patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult)); + patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult)); - CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); - prototypeStructures->list[currentIndex].set(*m_globalData, m_codeBlock->ownerExecutable(), entryLabel, structure, prototypeStructure); + MacroAssemblerCodeRef stubCode = patchBuffer.finalizeCode(); + prototypeStructures->list[currentIndex].set(*m_globalData, m_codeBlock->ownerExecutable(), stubCode, structure, prototypeStructure, isDirect); // Finally patch the jump to slow case back in the hot path to jump here instead. - CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase); + CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck); RepatchBuffer repatchBuffer(m_codeBlock); - repatchBuffer.relink(jumpLocation, entryLabel); + repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubCode.code())); } void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, const Identifier& ident, const PropertySlot& slot, size_t cachedOffset, CallFrame* callFrame) @@ -883,6 +839,7 @@ void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, Polymorphi ASSERT(protoObject); bool needsStubLink = false; + bool isDirect = false; if (slot.cachedPropertyType() == PropertySlot::Getter) { needsStubLink = true; compileGetDirectOffset(protoObject, regT1, cachedOffset); @@ -899,11 +856,13 @@ void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, Polymorphi stubCall.addArgument(TrustedImmPtr(const_cast(&ident))); stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); stubCall.call(); - } else + } else { + isDirect = true; compileGetDirectOffset(protoObject, regT0, cachedOffset); + } Jump success = jump(); - LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock->executablePool()); + LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock); if (needsStubLink) { for (Vector::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) { @@ -913,22 +872,22 @@ void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, Polymorphi } // Use the patch information to link the failure cases back to the original slow case routine. - CodeLocationLabel lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine; + CodeLocationLabel lastProtoBegin = CodeLocationLabel(prototypeStructures->list[currentIndex - 1].stubRoutine.code()); 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(patchOffsetGetByIdPutResult)); + patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult)); - CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); + CodeRef stubRoutine = patchBuffer.finalizeCode(); // Track the stub we have created so that it will be deleted later. - prototypeStructures->list[currentIndex].set(callFrame->globalData(), m_codeBlock->ownerExecutable(), entryLabel, structure, chain); + prototypeStructures->list[currentIndex].set(callFrame->globalData(), 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(patchOffsetGetByIdBranchToSlowCase); + CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck); RepatchBuffer repatchBuffer(m_codeBlock); - repatchBuffer.relink(jumpLocation, entryLabel); + repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine.code())); } void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, const Identifier& ident, const PropertySlot& slot, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame) @@ -971,7 +930,7 @@ void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* str compileGetDirectOffset(protoObject, regT0, cachedOffset); Jump success = jump(); - LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock->executablePool()); + LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock); if (needsStubLink) { for (Vector::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) { @@ -981,30 +940,175 @@ void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* str } // Use the patch information to link the failure cases back to the original slow case routine. - patchBuffer.link(bucketsOfFail, stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall)); + 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(patchOffsetGetByIdPutResult)); + 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. - CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum(); - stubInfo->stubRoutine = entryLabel; + CodeRef stubRoutine = patchBuffer.finalizeCode(); + stubInfo->stubRoutine = stubRoutine; // Finally patch the jump to slow case back in the hot path to jump here instead. - CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase); + CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck); RepatchBuffer repatchBuffer(m_codeBlock); - repatchBuffer.relink(jumpLocation, entryLabel); + repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine.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)); } -/* ------------------------------ END: !ENABLE / ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) ------------------------------ */ +void JIT::emit_op_get_scoped_var(Instruction* currentInstruction) +{ + int skip = currentInstruction[3].u.operand; + + emitGetFromCallFrameHeaderPtr(RegisterFile::ScopeChain, regT0); + bool checkTopLevel = m_codeBlock->codeType() == FunctionCode && m_codeBlock->needsFullScopeChain(); + ASSERT(skip || !checkTopLevel); + if (checkTopLevel && skip--) { + Jump activationNotCreated; + if (checkTopLevel) + activationNotCreated = branchTestPtr(Zero, addressFor(m_codeBlock->activationRegister())); + loadPtr(Address(regT0, OBJECT_OFFSETOF(ScopeChainNode, next)), regT0); + activationNotCreated.link(this); + } + while (skip--) + loadPtr(Address(regT0, OBJECT_OFFSETOF(ScopeChainNode, next)), regT0); + + loadPtr(Address(regT0, OBJECT_OFFSETOF(ScopeChainNode, object)), regT0); + loadPtr(Address(regT0, JSVariableObject::offsetOfRegisters()), regT0); + loadPtr(Address(regT0, currentInstruction[2].u.operand * sizeof(Register)), regT0); + emitValueProfilingSite(); + emitPutVirtualRegister(currentInstruction[1].u.operand); +} + +void JIT::emit_op_put_scoped_var(Instruction* currentInstruction) +{ + int skip = currentInstruction[2].u.operand; + + emitGetVirtualRegister(currentInstruction[3].u.operand, regT0); + + emitGetFromCallFrameHeaderPtr(RegisterFile::ScopeChain, regT1); + bool checkTopLevel = m_codeBlock->codeType() == FunctionCode && m_codeBlock->needsFullScopeChain(); + ASSERT(skip || !checkTopLevel); + if (checkTopLevel && skip--) { + Jump activationNotCreated; + if (checkTopLevel) + activationNotCreated = branchTestPtr(Zero, addressFor(m_codeBlock->activationRegister())); + loadPtr(Address(regT1, OBJECT_OFFSETOF(ScopeChainNode, next)), regT1); + activationNotCreated.link(this); + } + while (skip--) + loadPtr(Address(regT1, OBJECT_OFFSETOF(ScopeChainNode, next)), regT1); + loadPtr(Address(regT1, OBJECT_OFFSETOF(ScopeChainNode, object)), regT1); -#endif // !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) + emitWriteBarrier(regT1, regT0, regT2, regT3, ShouldFilterImmediates, WriteBarrierForVariableAccess); + + loadPtr(Address(regT1, JSVariableObject::offsetOfRegisters()), regT1); + storePtr(regT0, Address(regT1, currentInstruction[1].u.operand * sizeof(Register))); +} + +void JIT::emit_op_get_global_var(Instruction* currentInstruction) +{ + JSVariableObject* globalObject = m_codeBlock->globalObject(); + loadPtr(&globalObject->m_registers, regT0); + loadPtr(Address(regT0, currentInstruction[2].u.operand * sizeof(Register)), regT0); + emitValueProfilingSite(); + emitPutVirtualRegister(currentInstruction[1].u.operand); +} + +void JIT::emit_op_put_global_var(Instruction* currentInstruction) +{ + JSGlobalObject* globalObject = m_codeBlock->globalObject(); + + emitGetVirtualRegister(currentInstruction[2].u.operand, regT0); + + move(TrustedImmPtr(globalObject), regT1); + loadPtr(Address(regT1, JSVariableObject::offsetOfRegisters()), regT1); + storePtr(regT0, Address(regT1, currentInstruction[1].u.operand * sizeof(Register))); + emitWriteBarrier(globalObject, regT0, regT2, ShouldFilterImmediates, WriteBarrierForVariableAccess); +} + +void JIT::resetPatchGetById(RepatchBuffer& repatchBuffer, StructureStubInfo* stubInfo) +{ + repatchBuffer.relink(stubInfo->callReturnLocation, cti_op_get_by_id); + repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.get.structureToCompare), reinterpret_cast(-1)); + repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelCompactAtOffset(stubInfo->patch.baseline.u.get.displacementLabel), 0); + repatchBuffer.relink(stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck), stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin)); +} + +void JIT::resetPatchPutById(RepatchBuffer& repatchBuffer, StructureStubInfo* stubInfo) +{ + if (isDirectPutById(stubInfo)) + repatchBuffer.relink(stubInfo->callReturnLocation, cti_op_put_by_id_direct); + else + repatchBuffer.relink(stubInfo->callReturnLocation, cti_op_put_by_id); + repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.put.structureToCompare), reinterpret_cast(-1)); + repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelCompactAtOffset(stubInfo->patch.baseline.u.put.displacementLabel), 0); +} #endif // USE(JSVALUE64) +void JIT::emitWriteBarrier(RegisterID owner, RegisterID value, RegisterID scratch, RegisterID scratch2, WriteBarrierMode mode, WriteBarrierUseKind useKind) +{ + UNUSED_PARAM(owner); + UNUSED_PARAM(scratch); + UNUSED_PARAM(scratch2); + UNUSED_PARAM(useKind); + UNUSED_PARAM(value); + UNUSED_PARAM(mode); + ASSERT(owner != scratch); + ASSERT(owner != scratch2); + +#if ENABLE(WRITE_BARRIER_PROFILING) + emitCount(WriteBarrierCounters::jitCounterFor(useKind)); +#endif + +#if ENABLE(GGC) + Jump filterCells; + if (mode == ShouldFilterImmediates) + filterCells = emitJumpIfNotJSCell(value); + move(owner, scratch); + andPtr(TrustedImm32(static_cast(MarkedBlock::blockMask)), scratch); + move(owner, scratch2); + // consume additional 8 bits as we're using an approximate filter + rshift32(TrustedImm32(MarkedBlock::atomShift + 8), scratch2); + andPtr(TrustedImm32(MarkedBlock::atomMask >> 8), scratch2); + Jump filter = branchTest8(Zero, BaseIndex(scratch, scratch2, TimesOne, MarkedBlock::offsetOfMarks())); + move(owner, scratch2); + rshift32(TrustedImm32(MarkedBlock::cardShift), scratch2); + andPtr(TrustedImm32(MarkedBlock::cardMask), scratch2); + store8(TrustedImm32(1), BaseIndex(scratch, scratch2, TimesOne, MarkedBlock::offsetOfCards())); + filter.link(this); + if (mode == ShouldFilterImmediates) + filterCells.link(this); +#endif +} + +void JIT::emitWriteBarrier(JSCell* owner, RegisterID value, RegisterID scratch, WriteBarrierMode mode, WriteBarrierUseKind useKind) +{ + UNUSED_PARAM(owner); + UNUSED_PARAM(scratch); + UNUSED_PARAM(useKind); + UNUSED_PARAM(value); + UNUSED_PARAM(mode); + +#if ENABLE(WRITE_BARRIER_PROFILING) + emitCount(WriteBarrierCounters::jitCounterFor(useKind)); +#endif + +#if ENABLE(GGC) + Jump filterCells; + if (mode == ShouldFilterImmediates) + filterCells = emitJumpIfNotJSCell(value); + uint8_t* cardAddress = Heap::addressOfCardFor(owner); + move(TrustedImmPtr(cardAddress), scratch); + store8(TrustedImm32(1), Address(scratch)); + if (mode == ShouldFilterImmediates) + filterCells.link(this); +#endif +} + void JIT::testPrototype(JSValue prototype, JumpList& failureCases) { if (prototype.isNull()) @@ -1015,19 +1119,43 @@ void JIT::testPrototype(JSValue prototype, JumpList& failureCases) failureCases.append(branchPtr(NotEqual, Address(regT3, JSCell::structureOffset()), TrustedImmPtr(prototype.asCell()->structure()))); } -void JIT::patchMethodCallProto(JSGlobalData& globalData, CodeBlock* codeBlock, MethodCallLinkInfo& methodCallLinkInfo, JSObjectWithGlobalObject* callee, Structure* structure, JSObject* proto, ReturnAddressPtr returnAddress) +void JIT::patchMethodCallProto(JSGlobalData& globalData, CodeBlock* codeBlock, MethodCallLinkInfo& methodCallLinkInfo, StructureStubInfo& stubInfo, JSObject* callee, Structure* structure, JSObject* proto, ReturnAddressPtr returnAddress) { RepatchBuffer repatchBuffer(codeBlock); - ASSERT(!methodCallLinkInfo.cachedStructure); CodeLocationDataLabelPtr structureLocation = methodCallLinkInfo.cachedStructure.location(); methodCallLinkInfo.cachedStructure.set(globalData, structureLocation, codeBlock->ownerExecutable(), structure); Structure* prototypeStructure = proto->structure(); - methodCallLinkInfo.cachedPrototypeStructure.set(globalData, structureLocation.dataLabelPtrAtOffset(patchOffsetMethodCheckProtoStruct), codeBlock->ownerExecutable(), prototypeStructure); - methodCallLinkInfo.cachedPrototype.set(globalData, structureLocation.dataLabelPtrAtOffset(patchOffsetMethodCheckProtoObj), codeBlock->ownerExecutable(), proto); - methodCallLinkInfo.cachedFunction.set(globalData, structureLocation.dataLabelPtrAtOffset(patchOffsetMethodCheckPutFunction), codeBlock->ownerExecutable(), callee); - repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id)); + methodCallLinkInfo.cachedPrototypeStructure.set(globalData, structureLocation.dataLabelPtrAtOffset(stubInfo.patch.baseline.methodCheckProtoStructureToCompare), codeBlock->ownerExecutable(), prototypeStructure); + methodCallLinkInfo.cachedPrototype.set(globalData, structureLocation.dataLabelPtrAtOffset(stubInfo.patch.baseline.methodCheckProtoObj), codeBlock->ownerExecutable(), proto); + methodCallLinkInfo.cachedFunction.set(globalData, structureLocation.dataLabelPtrAtOffset(stubInfo.patch.baseline.methodCheckPutFunction), codeBlock->ownerExecutable(), callee); + repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_method_check_update)); +} + +bool JIT::isDirectPutById(StructureStubInfo* stubInfo) +{ + switch (stubInfo->accessType) { + case access_put_by_id_transition_normal: + return false; + case access_put_by_id_transition_direct: + return true; + case access_put_by_id_replace: + case access_put_by_id_generic: { + void* oldCall = MacroAssembler::readCallTarget(stubInfo->callReturnLocation).executableAddress(); + if (oldCall == bitwise_cast(cti_op_put_by_id_direct) + || oldCall == bitwise_cast(cti_op_put_by_id_direct_generic) + || oldCall == bitwise_cast(cti_op_put_by_id_direct_fail)) + return true; + ASSERT(oldCall == bitwise_cast(cti_op_put_by_id) + || oldCall == bitwise_cast(cti_op_put_by_id_generic) + || oldCall == bitwise_cast(cti_op_put_by_id_fail)); + return false; + } + default: + ASSERT_NOT_REACHED(); + return false; + } } } // namespace JSC