diff options
Diffstat (limited to 'Source/JavaScriptCore/jit/JITPropertyAccess.cpp')
-rw-r--r-- | Source/JavaScriptCore/jit/JITPropertyAccess.cpp | 155 |
1 files changed, 54 insertions, 101 deletions
diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp index b7be821f6..6362598f4 100644 --- a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp +++ b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp @@ -440,71 +440,6 @@ void JIT::emit_op_del_by_id(Instruction* currentInstruction) stubCall.call(currentInstruction[1].u.operand); } -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 - || m_interpreter->getOpcodeID((currentInstruction + OPCODE_LENGTH(op_method_check))->u.opcode) == op_get_by_id_out_of_line); - - 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_bytecodeOffset, m_propertyAccessCompilationInfo.size())); - MethodCallCompilationInfo& info = m_methodCallCompilationInfo.last(); - - Jump notCell = emitJumpIfNotJSCell(regT0); - - BEGIN_UNINTERRUPTED_SEQUENCE(sequenceMethodCheck); - - Jump structureCheck = branchPtrWithPatch(NotEqual, Address(regT0, JSCell::structureOffset()), info.structureToCompare, TrustedImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure))); - DataLabelPtr protoStructureToCompare, protoObj = moveWithPatch(TrustedImmPtr(0), regT1); - Jump protoStructureCheck = branchPtrWithPatch(NotEqual, Address(regT1, JSCell::structureOffset()), protoStructureToCompare, TrustedImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure))); - - // This will be relinked to load the function without doing a load. - DataLabelPtr putFunction = moveWithPatch(TrustedImmPtr(0), regT0); - - END_UNINTERRUPTED_SEQUENCE(sequenceMethodCheck); - - Jump match = jump(); - - // 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(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<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, 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); -} - void JIT::emit_op_get_by_id(Instruction* currentInstruction) { unsigned resultVReg = currentInstruction[1].u.operand; @@ -517,7 +452,7 @@ void JIT::emit_op_get_by_id(Instruction* currentInstruction) emitPutVirtualRegister(resultVReg); } -void JIT::compileGetByIdHotPath(int baseVReg, Identifier*) +void JIT::compileGetByIdHotPath(int baseVReg, Identifier* ident) { // 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 @@ -525,6 +460,11 @@ void JIT::compileGetByIdHotPath(int baseVReg, Identifier*) // to jump back to if one of these trampolies finds a match. emitJumpSlowCaseIfNotJSCell(regT0, baseVReg); + + if (*ident == m_globalData->propertyNames->length && canBeOptimized()) { + loadPtr(Address(regT0, JSCell::structureOffset()), regT1); + emitArrayProfilingSiteForBytecodeIndex(regT1, regT2, m_bytecodeOffset); + } BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath); @@ -550,11 +490,11 @@ void JIT::emitSlow_op_get_by_id(Instruction* currentInstruction, Vector<SlowCase unsigned baseVReg = currentInstruction[2].u.operand; Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand)); - compileGetByIdSlowCase(resultVReg, baseVReg, ident, iter, false); + compileGetByIdSlowCase(resultVReg, baseVReg, ident, iter); emitValueProfilingSite(); } -void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident, Vector<SlowCaseEntry>::iterator& iter, bool isMethodCheck) +void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident, Vector<SlowCaseEntry>::iterator& iter) { // 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 @@ -568,7 +508,7 @@ void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdSlowCase); Label coldPathBegin(this); - JITStubCall stubCall(this, isMethodCheck ? cti_op_get_by_id_method_check : cti_op_get_by_id); + JITStubCall stubCall(this, cti_op_get_by_id); stubCall.addArgument(regT0); stubCall.addArgument(TrustedImmPtr(ident)); Call call = stubCall.call(resultVReg); @@ -676,7 +616,7 @@ void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure failureCases.append(emitJumpIfNotJSCell(regT0)); failureCases.append(branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), TrustedImmPtr(oldStructure))); - testPrototype(oldStructure->storedPrototype(), failureCases); + testPrototype(oldStructure->storedPrototype(), failureCases, stubInfo); ASSERT(oldStructure->storedPrototype().isNull() || oldStructure->storedPrototype().asCell()->structure() == chain->head()->get()); @@ -684,7 +624,7 @@ void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure if (!direct) { for (WriteBarrier<Structure>* it = chain->head(); *it; ++it) { ASSERT((*it)->storedPrototype().isNull() || (*it)->storedPrototype().asCell()->structure() == it[1].get()); - testPrototype((*it)->storedPrototype(), failureCases); + testPrototype((*it)->storedPrototype(), failureCases, stubInfo); } } @@ -788,7 +728,6 @@ void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress) // Check eax is an array loadPtr(Address(regT0, JSCell::structureOffset()), regT2); - emitArrayProfilingSiteForBytecodeIndex(regT2, regT1, stubInfo->bytecodeIndex); Jump failureCases1 = branchTest32(Zero, regT2, TrustedImm32(IsArray)); Jump failureCases2 = branchTest32(Zero, regT2, TrustedImm32(IndexingShapeMask)); @@ -837,8 +776,7 @@ void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* str Jump failureCases1 = checkStructure(regT0, structure); // Check the prototype object's Structure had not changed. - move(TrustedImmPtr(protoObject), regT3); - Jump failureCases2 = branchPtr(NotEqual, Address(regT3, JSCell::structureOffset()), TrustedImmPtr(prototypeStructure)); + Jump failureCases2 = addStructureTransitionCheck(protoObject, prototypeStructure, stubInfo, regT3); bool needsStubLink = false; @@ -867,7 +805,8 @@ void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* str // Use the patch information to link the failure cases back to the original slow case routine. CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin); patchBuffer.link(failureCases1, slowCaseBegin); - patchBuffer.link(failureCases2, slowCaseBegin); + if (failureCases2.isSet()) + patchBuffer.link(failureCases2, slowCaseBegin); // On success return back to the hot patch code, at a point it will perform the store to dest for us. patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult)); @@ -972,8 +911,7 @@ void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, Polymorphi Jump failureCases1 = checkStructure(regT0, structure); // Check the prototype object's Structure had not changed. - move(TrustedImmPtr(protoObject), regT3); - Jump failureCases2 = branchPtr(NotEqual, Address(regT3, JSCell::structureOffset()), TrustedImmPtr(prototypeStructure)); + Jump failureCases2 = addStructureTransitionCheck(protoObject, prototypeStructure, stubInfo, regT3); // Checks out okay! bool needsStubLink = false; @@ -1013,7 +951,8 @@ void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, Polymorphi // Use the patch information to link the failure cases back to the original slow case routine. CodeLocationLabel lastProtoBegin = CodeLocationLabel(JITStubRoutine::asCodePtr(prototypeStructures->list[currentIndex - 1].stubRoutine)); patchBuffer.link(failureCases1, lastProtoBegin); - patchBuffer.link(failureCases2, lastProtoBegin); + if (failureCases2.isSet()) + patchBuffer.link(failureCases2, lastProtoBegin); // On success return back to the hot patch code, at a point it will perform the store to dest for us. patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult)); @@ -1050,7 +989,7 @@ void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, Polymorphi for (unsigned i = 0; i < count; ++i, ++it) { protoObject = asObject(currStructure->prototypeForLookup(callFrame)); currStructure = it->get(); - testPrototype(protoObject, bucketsOfFail); + testPrototype(protoObject, bucketsOfFail, stubInfo); } ASSERT(protoObject); @@ -1129,7 +1068,7 @@ void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* str for (unsigned i = 0; i < count; ++i, ++it) { protoObject = asObject(currStructure->prototypeForLookup(callFrame)); currStructure = it->get(); - testPrototype(protoObject, bucketsOfFail); + testPrototype(protoObject, bucketsOfFail, stubInfo); } ASSERT(protoObject); @@ -1304,28 +1243,40 @@ void JIT::emitWriteBarrier(JSCell* owner, RegisterID value, RegisterID scratch, #endif } -void JIT::testPrototype(JSValue prototype, JumpList& failureCases) +JIT::Jump JIT::addStructureTransitionCheck(JSCell* object, Structure* structure, StructureStubInfo* stubInfo, RegisterID scratch) { - if (prototype.isNull()) - return; - - ASSERT(prototype.isCell()); - move(TrustedImmPtr(prototype.asCell()), regT3); - failureCases.append(branchPtr(NotEqual, Address(regT3, JSCell::structureOffset()), TrustedImmPtr(prototype.asCell()->structure()))); + if (object->structure() == structure && structure->transitionWatchpointSetIsStillValid()) { + structure->addTransitionWatchpoint(stubInfo->addWatchpoint(m_codeBlock)); +#if !ASSERT_DISABLED + move(TrustedImmPtr(object), scratch); + Jump ok = branchPtr(Equal, Address(scratch, JSCell::structureOffset()), TrustedImmPtr(structure)); + breakpoint(); + ok.link(this); +#endif + Jump result; // Returning an unset jump this way because otherwise VC++ would complain. + return result; + } + + move(TrustedImmPtr(object), scratch); + return branchPtr(NotEqual, Address(scratch, JSCell::structureOffset()), TrustedImmPtr(structure)); } -void JIT::patchMethodCallProto(JSGlobalData& globalData, CodeBlock* codeBlock, MethodCallLinkInfo& methodCallLinkInfo, StructureStubInfo& stubInfo, JSObject* callee, Structure* structure, JSObject* proto, ReturnAddressPtr returnAddress) +void JIT::addStructureTransitionCheck(JSCell* object, Structure* structure, StructureStubInfo* stubInfo, JumpList& failureCases, RegisterID scratch) { - RepatchBuffer repatchBuffer(codeBlock); - - CodeLocationDataLabelPtr structureLocation = methodCallLinkInfo.cachedStructure.location(); - methodCallLinkInfo.cachedStructure.set(globalData, structureLocation, codeBlock->ownerExecutable(), structure); + Jump failureCase = addStructureTransitionCheck(object, structure, stubInfo, scratch); + if (!failureCase.isSet()) + return; - Structure* prototypeStructure = proto->structure(); - 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)); + failureCases.append(failureCase); +} + +void JIT::testPrototype(JSValue prototype, JumpList& failureCases, StructureStubInfo* stubInfo) +{ + if (prototype.isNull()) + return; + + ASSERT(prototype.isCell()); + addStructureTransitionCheck(prototype.asCell(), prototype.asCell()->structure(), stubInfo, failureCases, regT3); } bool JIT::isDirectPutById(StructureStubInfo* stubInfo) @@ -1377,9 +1328,11 @@ void JIT::privateCompileGetByVal(ByValInfo* byValInfo, ReturnAddressPtr returnAd slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_globalData->int32ArrayDescriptor(), 4, SignedTypedArray); break; case JITUint8Array: - case JITUint8ClampedArray: slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_globalData->uint8ArrayDescriptor(), 1, UnsignedTypedArray); break; + case JITUint8ClampedArray: + slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_globalData->uint8ClampedArrayDescriptor(), 1, UnsignedTypedArray); + break; case JITUint16Array: slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_globalData->uint16ArrayDescriptor(), 2, UnsignedTypedArray); break; @@ -1400,8 +1353,8 @@ void JIT::privateCompileGetByVal(ByValInfo* byValInfo, ReturnAddressPtr returnAd LinkBuffer patchBuffer(*m_globalData, this, m_codeBlock); - patchBuffer.link(badType, CodeLocationLabel(returnAddress.value()).labelAtOffset(byValInfo->returnAddressToSlowPath)); - patchBuffer.link(slowCases, CodeLocationLabel(returnAddress.value()).labelAtOffset(byValInfo->returnAddressToSlowPath)); + patchBuffer.link(badType, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(returnAddress.value())).labelAtOffset(byValInfo->returnAddressToSlowPath)); + patchBuffer.link(slowCases, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(returnAddress.value())).labelAtOffset(byValInfo->returnAddressToSlowPath)); patchBuffer.link(done, byValInfo->badTypeJump.labelAtOffset(byValInfo->badTypeJumpToDone)); @@ -1580,7 +1533,7 @@ JIT::JumpList JIT::emitFloatTypedArrayGetByVal(Instruction*, PatchableJump& badT case 8: { loadDouble(BaseIndex(base, property, TimesEight), fpRegT0); Jump notNaN = branchDouble(DoubleEqual, fpRegT0, fpRegT0); - static const double NaN = std::numeric_limits<double>::quiet_NaN(); + static const double NaN = QNaN; loadDouble(&NaN, fpRegT0); notNaN.link(this); break; |