summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/jit/Repatch.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2015-10-15 09:45:50 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2015-10-15 09:45:50 +0000
commite15dd966d523731101f70ccf768bba12435a0208 (patch)
treeae9cb828a24ded2585a41af3f21411523b47897d /Source/JavaScriptCore/jit/Repatch.cpp
downloadWebKitGtk-tarball-e15dd966d523731101f70ccf768bba12435a0208.tar.gz
webkitgtk-2.10.2webkitgtk-2.10.2
Diffstat (limited to 'Source/JavaScriptCore/jit/Repatch.cpp')
-rw-r--r--Source/JavaScriptCore/jit/Repatch.cpp1998
1 files changed, 1998 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/jit/Repatch.cpp b/Source/JavaScriptCore/jit/Repatch.cpp
new file mode 100644
index 000000000..762f39145
--- /dev/null
+++ b/Source/JavaScriptCore/jit/Repatch.cpp
@@ -0,0 +1,1998 @@
+/*
+ * Copyright (C) 2011-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Repatch.h"
+
+#if ENABLE(JIT)
+
+#include "AccessorCallJITStubRoutine.h"
+#include "BinarySwitch.h"
+#include "CCallHelpers.h"
+#include "DFGOperations.h"
+#include "DFGSpeculativeJIT.h"
+#include "FTLThunks.h"
+#include "GCAwareJITStubRoutine.h"
+#include "GetterSetter.h"
+#include "JIT.h"
+#include "JITInlines.h"
+#include "LinkBuffer.h"
+#include "JSCInlines.h"
+#include "PolymorphicGetByIdList.h"
+#include "PolymorphicPutByIdList.h"
+#include "RegExpMatchesArray.h"
+#include "RepatchBuffer.h"
+#include "ScratchRegisterAllocator.h"
+#include "StackAlignment.h"
+#include "StructureRareDataInlines.h"
+#include "StructureStubClearingWatchpoint.h"
+#include "ThunkGenerators.h"
+#include <wtf/CommaPrinter.h>
+#include <wtf/ListDump.h>
+#include <wtf/StringPrintStream.h>
+
+namespace JSC {
+
+// Beware: in this code, it is not safe to assume anything about the following registers
+// that would ordinarily have well-known values:
+// - tagTypeNumberRegister
+// - tagMaskRegister
+
+static FunctionPtr readCallTarget(RepatchBuffer& repatchBuffer, CodeLocationCall call)
+{
+ FunctionPtr result = MacroAssembler::readCallTarget(call);
+#if ENABLE(FTL_JIT)
+ CodeBlock* codeBlock = repatchBuffer.codeBlock();
+ if (codeBlock->jitType() == JITCode::FTLJIT) {
+ return FunctionPtr(codeBlock->vm()->ftlThunks->keyForSlowPathCallThunk(
+ MacroAssemblerCodePtr::createFromExecutableAddress(
+ result.executableAddress())).callTarget());
+ }
+#else
+ UNUSED_PARAM(repatchBuffer);
+#endif // ENABLE(FTL_JIT)
+ return result;
+}
+
+static void repatchCall(RepatchBuffer& repatchBuffer, CodeLocationCall call, FunctionPtr newCalleeFunction)
+{
+#if ENABLE(FTL_JIT)
+ CodeBlock* codeBlock = repatchBuffer.codeBlock();
+ if (codeBlock->jitType() == JITCode::FTLJIT) {
+ VM& vm = *codeBlock->vm();
+ FTL::Thunks& thunks = *vm.ftlThunks;
+ FTL::SlowPathCallKey key = thunks.keyForSlowPathCallThunk(
+ MacroAssemblerCodePtr::createFromExecutableAddress(
+ MacroAssembler::readCallTarget(call).executableAddress()));
+ key = key.withCallTarget(newCalleeFunction.executableAddress());
+ newCalleeFunction = FunctionPtr(
+ thunks.getSlowPathCallThunk(vm, key).code().executableAddress());
+ }
+#endif // ENABLE(FTL_JIT)
+ repatchBuffer.relink(call, newCalleeFunction);
+}
+
+static void repatchCall(CodeBlock* codeblock, CodeLocationCall call, FunctionPtr newCalleeFunction)
+{
+ RepatchBuffer repatchBuffer(codeblock);
+ repatchCall(repatchBuffer, call, newCalleeFunction);
+}
+
+static void repatchByIdSelfAccess(
+ VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo, Structure* structure,
+ const Identifier& propertyName, PropertyOffset offset, const FunctionPtr &slowPathFunction,
+ bool compact)
+{
+ if (structure->needImpurePropertyWatchpoint())
+ vm.registerWatchpointForImpureProperty(propertyName, stubInfo.addWatchpoint(codeBlock));
+
+ RepatchBuffer repatchBuffer(codeBlock);
+
+ // Only optimize once!
+ repatchCall(repatchBuffer, stubInfo.callReturnLocation, slowPathFunction);
+
+ // Patch the structure check & the offset of the load.
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(-(intptr_t)stubInfo.patch.deltaCheckImmToCall), bitwise_cast<int32_t>(structure->id()));
+ repatchBuffer.setLoadInstructionIsActive(stubInfo.callReturnLocation.convertibleLoadAtOffset(stubInfo.patch.deltaCallToStorageLoad), isOutOfLineOffset(offset));
+#if USE(JSVALUE64)
+ if (compact)
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.deltaCallToLoadOrStore), offsetRelativeToPatchedStorage(offset));
+ else
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.deltaCallToLoadOrStore), offsetRelativeToPatchedStorage(offset));
+#elif USE(JSVALUE32_64)
+ if (compact) {
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.deltaCallToTagLoadOrStore), offsetRelativeToPatchedStorage(offset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.deltaCallToPayloadLoadOrStore), offsetRelativeToPatchedStorage(offset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
+ } else {
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.deltaCallToTagLoadOrStore), offsetRelativeToPatchedStorage(offset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.deltaCallToPayloadLoadOrStore), offsetRelativeToPatchedStorage(offset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
+ }
+#endif
+}
+
+static void checkObjectPropertyCondition(
+ const ObjectPropertyCondition& condition, CodeBlock* codeBlock, StructureStubInfo& stubInfo,
+ MacroAssembler& jit, MacroAssembler::JumpList& failureCases, GPRReg scratchGPR)
+{
+ if (condition.isWatchableAssumingImpurePropertyWatchpoint()) {
+ condition.object()->structure()->addTransitionWatchpoint(
+ stubInfo.addWatchpoint(codeBlock, condition));
+ return;
+ }
+
+ Structure* structure = condition.object()->structure();
+ RELEASE_ASSERT(condition.structureEnsuresValidityAssumingImpurePropertyWatchpoint(structure));
+ jit.move(MacroAssembler::TrustedImmPtr(condition.object()), scratchGPR);
+ failureCases.append(
+ branchStructure(
+ jit, MacroAssembler::NotEqual,
+ MacroAssembler::Address(scratchGPR, JSCell::structureIDOffset()), structure));
+}
+
+static void checkObjectPropertyConditions(
+ const ObjectPropertyConditionSet& set, CodeBlock* codeBlock, StructureStubInfo& stubInfo,
+ MacroAssembler& jit, MacroAssembler::JumpList& failureCases, GPRReg scratchGPR)
+{
+ for (const ObjectPropertyCondition& condition : set) {
+ checkObjectPropertyCondition(
+ condition, codeBlock, stubInfo, jit, failureCases, scratchGPR);
+ }
+}
+
+static void replaceWithJump(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo, const MacroAssemblerCodePtr target)
+{
+ if (MacroAssembler::canJumpReplacePatchableBranch32WithPatch()) {
+ repatchBuffer.replaceWithJump(
+ RepatchBuffer::startOfPatchableBranch32WithPatchOnAddress(
+ stubInfo.callReturnLocation.dataLabel32AtOffset(
+ -(intptr_t)stubInfo.patch.deltaCheckImmToCall)),
+ CodeLocationLabel(target));
+ return;
+ }
+
+ repatchBuffer.relink(
+ stubInfo.callReturnLocation.jumpAtOffset(
+ stubInfo.patch.deltaCallToJump),
+ CodeLocationLabel(target));
+}
+
+static void emitRestoreScratch(MacroAssembler& stubJit, bool needToRestoreScratch, GPRReg scratchGPR, MacroAssembler::Jump& success, MacroAssembler::Jump& fail, MacroAssembler::JumpList failureCases)
+{
+ if (needToRestoreScratch) {
+ stubJit.popToRestore(scratchGPR);
+
+ success = stubJit.jump();
+
+ // link failure cases here, so we can pop scratchGPR, and then jump back.
+ failureCases.link(&stubJit);
+
+ stubJit.popToRestore(scratchGPR);
+
+ fail = stubJit.jump();
+ return;
+ }
+
+ success = stubJit.jump();
+}
+
+static void linkRestoreScratch(LinkBuffer& patchBuffer, bool needToRestoreScratch, MacroAssembler::Jump success, MacroAssembler::Jump fail, MacroAssembler::JumpList failureCases, CodeLocationLabel successLabel, CodeLocationLabel slowCaseBegin)
+{
+ patchBuffer.link(success, successLabel);
+
+ if (needToRestoreScratch) {
+ patchBuffer.link(fail, slowCaseBegin);
+ return;
+ }
+
+ // link failure cases directly back to normal path
+ patchBuffer.link(failureCases, slowCaseBegin);
+}
+
+static void linkRestoreScratch(LinkBuffer& patchBuffer, bool needToRestoreScratch, StructureStubInfo& stubInfo, MacroAssembler::Jump success, MacroAssembler::Jump fail, MacroAssembler::JumpList failureCases)
+{
+ linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
+}
+
+enum ByIdStubKind {
+ GetValue,
+ GetUndefined,
+ CallGetter,
+ CallCustomGetter,
+ CallSetter,
+ CallCustomSetter
+};
+
+static const char* toString(ByIdStubKind kind)
+{
+ switch (kind) {
+ case GetValue:
+ return "GetValue";
+ case GetUndefined:
+ return "GetUndefined";
+ case CallGetter:
+ return "CallGetter";
+ case CallCustomGetter:
+ return "CallCustomGetter";
+ case CallSetter:
+ return "CallSetter";
+ case CallCustomSetter:
+ return "CallCustomSetter";
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ return nullptr;
+ }
+}
+
+static ByIdStubKind kindFor(const PropertySlot& slot)
+{
+ if (slot.isCacheableValue())
+ return GetValue;
+ if (slot.isUnset())
+ return GetUndefined;
+ if (slot.isCacheableCustom())
+ return CallCustomGetter;
+ RELEASE_ASSERT(slot.isCacheableGetter());
+ return CallGetter;
+}
+
+static FunctionPtr customFor(const PropertySlot& slot)
+{
+ if (!slot.isCacheableCustom())
+ return FunctionPtr();
+ return FunctionPtr(slot.customGetter());
+}
+
+static ByIdStubKind kindFor(const PutPropertySlot& slot)
+{
+ RELEASE_ASSERT(!slot.isCacheablePut());
+ if (slot.isCacheableSetter())
+ return CallSetter;
+ RELEASE_ASSERT(slot.isCacheableCustom());
+ return CallCustomSetter;
+}
+
+static FunctionPtr customFor(const PutPropertySlot& slot)
+{
+ if (!slot.isCacheableCustom())
+ return FunctionPtr();
+ return FunctionPtr(slot.customSetter());
+}
+
+static bool generateByIdStub(
+ ExecState* exec, ByIdStubKind kind, const Identifier& propertyName,
+ FunctionPtr custom, StructureStubInfo& stubInfo, const ObjectPropertyConditionSet& conditionSet,
+ JSObject* alternateBase, PropertyOffset offset, Structure* structure, bool loadTargetFromProxy,
+ WatchpointSet* watchpointSet, CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel,
+ RefPtr<JITStubRoutine>& stubRoutine)
+{
+ ASSERT(conditionSet.structuresEnsureValidityAssumingImpurePropertyWatchpoint());
+
+ VM* vm = &exec->vm();
+ GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
+ JSValueRegs valueRegs = JSValueRegs(
+#if USE(JSVALUE32_64)
+ static_cast<GPRReg>(stubInfo.patch.valueTagGPR),
+#endif
+ static_cast<GPRReg>(stubInfo.patch.valueGPR));
+ GPRReg scratchGPR = TempRegisterSet(stubInfo.patch.usedRegisters).getFreeGPR();
+ bool needToRestoreScratch = scratchGPR == InvalidGPRReg;
+ RELEASE_ASSERT(!needToRestoreScratch || (kind == GetValue || kind == GetUndefined));
+
+ CCallHelpers stubJit(&exec->vm(), exec->codeBlock());
+ if (needToRestoreScratch) {
+ scratchGPR = AssemblyHelpers::selectScratchGPR(
+ baseGPR, valueRegs.tagGPR(), valueRegs.payloadGPR());
+ stubJit.pushToSave(scratchGPR);
+ needToRestoreScratch = true;
+ }
+
+ MacroAssembler::JumpList failureCases;
+
+ GPRReg baseForGetGPR;
+ if (loadTargetFromProxy) {
+ baseForGetGPR = valueRegs.payloadGPR();
+ failureCases.append(stubJit.branch8(
+ MacroAssembler::NotEqual,
+ MacroAssembler::Address(baseGPR, JSCell::typeInfoTypeOffset()),
+ MacroAssembler::TrustedImm32(PureForwardingProxyType)));
+
+ stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSProxy::targetOffset()), scratchGPR);
+
+ failureCases.append(branchStructure(stubJit,
+ MacroAssembler::NotEqual,
+ MacroAssembler::Address(scratchGPR, JSCell::structureIDOffset()),
+ structure));
+ } else {
+ baseForGetGPR = baseGPR;
+
+ failureCases.append(branchStructure(stubJit,
+ MacroAssembler::NotEqual,
+ MacroAssembler::Address(baseForGetGPR, JSCell::structureIDOffset()),
+ structure));
+ }
+
+ CodeBlock* codeBlock = exec->codeBlock();
+ if (structure->needImpurePropertyWatchpoint() || conditionSet.needImpurePropertyWatchpoint())
+ vm->registerWatchpointForImpureProperty(propertyName, stubInfo.addWatchpoint(codeBlock));
+
+ if (watchpointSet)
+ watchpointSet->add(stubInfo.addWatchpoint(codeBlock));
+
+ checkObjectPropertyConditions(
+ conditionSet, codeBlock, stubInfo, stubJit, failureCases, scratchGPR);
+
+ if (isValidOffset(offset)) {
+ Structure* currStructure;
+ if (conditionSet.isEmpty())
+ currStructure = structure;
+ else
+ currStructure = conditionSet.slotBaseCondition().object()->structure();
+ currStructure->startWatchingPropertyForReplacements(*vm, offset);
+ }
+
+ GPRReg baseForAccessGPR = InvalidGPRReg;
+ if (kind != GetUndefined) {
+ if (!conditionSet.isEmpty()) {
+ // We could have clobbered scratchGPR earlier, so we have to reload from baseGPR to get the target.
+ if (loadTargetFromProxy)
+ stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSProxy::targetOffset()), baseForGetGPR);
+ stubJit.move(MacroAssembler::TrustedImmPtr(alternateBase), scratchGPR);
+ baseForAccessGPR = scratchGPR;
+ } else {
+ // For proxy objects, we need to do all the Structure checks before moving the baseGPR into
+ // baseForGetGPR because if we fail any of the checks then we would have the wrong value in baseGPR
+ // on the slow path.
+ if (loadTargetFromProxy)
+ stubJit.move(scratchGPR, baseForGetGPR);
+ baseForAccessGPR = baseForGetGPR;
+ }
+ }
+
+ GPRReg loadedValueGPR = InvalidGPRReg;
+ if (kind == GetUndefined)
+ stubJit.moveTrustedValue(jsUndefined(), valueRegs);
+ else if (kind != CallCustomGetter && kind != CallCustomSetter) {
+ if (kind == GetValue)
+ loadedValueGPR = valueRegs.payloadGPR();
+ else
+ loadedValueGPR = scratchGPR;
+
+ GPRReg storageGPR;
+ if (isInlineOffset(offset))
+ storageGPR = baseForAccessGPR;
+ else {
+ stubJit.loadPtr(MacroAssembler::Address(baseForAccessGPR, JSObject::butterflyOffset()), loadedValueGPR);
+ storageGPR = loadedValueGPR;
+ }
+
+#if USE(JSVALUE64)
+ stubJit.load64(MacroAssembler::Address(storageGPR, offsetRelativeToBase(offset)), loadedValueGPR);
+#else
+ if (kind == GetValue)
+ stubJit.load32(MacroAssembler::Address(storageGPR, offsetRelativeToBase(offset) + TagOffset), valueRegs.tagGPR());
+ stubJit.load32(MacroAssembler::Address(storageGPR, offsetRelativeToBase(offset) + PayloadOffset), loadedValueGPR);
+#endif
+ }
+
+ // Stuff for custom getters.
+ MacroAssembler::Call operationCall;
+ MacroAssembler::Call handlerCall;
+
+ // Stuff for JS getters.
+ MacroAssembler::DataLabelPtr addressOfLinkFunctionCheck;
+ MacroAssembler::Call fastPathCall;
+ MacroAssembler::Call slowPathCall;
+ std::unique_ptr<CallLinkInfo> callLinkInfo;
+
+ MacroAssembler::Jump success, fail;
+ if (kind != GetValue && kind != GetUndefined) {
+ // Need to make sure that whenever this call is made in the future, we remember the
+ // place that we made it from. It just so happens to be the place that we are at
+ // right now!
+ stubJit.store32(MacroAssembler::TrustedImm32(exec->locationAsRawBits()),
+ CCallHelpers::tagFor(static_cast<VirtualRegister>(JSStack::ArgumentCount)));
+
+ if (kind == CallGetter || kind == CallSetter) {
+ // Create a JS call using a JS call inline cache. Assume that:
+ //
+ // - SP is aligned and represents the extent of the calling compiler's stack usage.
+ //
+ // - FP is set correctly (i.e. it points to the caller's call frame header).
+ //
+ // - SP - FP is an aligned difference.
+ //
+ // - Any byte between FP (exclusive) and SP (inclusive) could be live in the calling
+ // code.
+ //
+ // Therefore, we temporarily grow the stack for the purpose of the call and then
+ // shrink it after.
+
+ callLinkInfo = std::make_unique<CallLinkInfo>();
+ callLinkInfo->setUpCall(CallLinkInfo::Call, stubInfo.codeOrigin, loadedValueGPR);
+
+ MacroAssembler::JumpList done;
+
+ // There is a 'this' argument but nothing else.
+ unsigned numberOfParameters = 1;
+ // ... unless we're calling a setter.
+ if (kind == CallSetter)
+ numberOfParameters++;
+
+ // Get the accessor; if there ain't one then the result is jsUndefined().
+ if (kind == CallSetter) {
+ stubJit.loadPtr(
+ MacroAssembler::Address(loadedValueGPR, GetterSetter::offsetOfSetter()),
+ loadedValueGPR);
+ } else {
+ stubJit.loadPtr(
+ MacroAssembler::Address(loadedValueGPR, GetterSetter::offsetOfGetter()),
+ loadedValueGPR);
+ }
+ MacroAssembler::Jump returnUndefined = stubJit.branchTestPtr(
+ MacroAssembler::Zero, loadedValueGPR);
+
+ unsigned numberOfRegsForCall =
+ JSStack::CallFrameHeaderSize + numberOfParameters;
+
+ unsigned numberOfBytesForCall =
+ numberOfRegsForCall * sizeof(Register) - sizeof(CallerFrameAndPC);
+
+ unsigned alignedNumberOfBytesForCall =
+ WTF::roundUpToMultipleOf(stackAlignmentBytes(), numberOfBytesForCall);
+
+ stubJit.subPtr(
+ MacroAssembler::TrustedImm32(alignedNumberOfBytesForCall),
+ MacroAssembler::stackPointerRegister);
+
+ MacroAssembler::Address calleeFrame = MacroAssembler::Address(
+ MacroAssembler::stackPointerRegister,
+ -static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC)));
+
+ stubJit.store32(
+ MacroAssembler::TrustedImm32(numberOfParameters),
+ calleeFrame.withOffset(
+ JSStack::ArgumentCount * sizeof(Register) + PayloadOffset));
+
+ stubJit.storeCell(
+ loadedValueGPR, calleeFrame.withOffset(JSStack::Callee * sizeof(Register)));
+
+ stubJit.storeCell(
+ baseForGetGPR,
+ calleeFrame.withOffset(
+ virtualRegisterForArgument(0).offset() * sizeof(Register)));
+
+ if (kind == CallSetter) {
+ stubJit.storeValue(
+ valueRegs,
+ calleeFrame.withOffset(
+ virtualRegisterForArgument(1).offset() * sizeof(Register)));
+ }
+
+ MacroAssembler::Jump slowCase = stubJit.branchPtrWithPatch(
+ MacroAssembler::NotEqual, loadedValueGPR, addressOfLinkFunctionCheck,
+ MacroAssembler::TrustedImmPtr(0));
+
+ fastPathCall = stubJit.nearCall();
+
+ stubJit.addPtr(
+ MacroAssembler::TrustedImm32(alignedNumberOfBytesForCall),
+ MacroAssembler::stackPointerRegister);
+ if (kind == CallGetter)
+ stubJit.setupResults(valueRegs);
+
+ done.append(stubJit.jump());
+ slowCase.link(&stubJit);
+
+ stubJit.move(loadedValueGPR, GPRInfo::regT0);
+#if USE(JSVALUE32_64)
+ stubJit.move(MacroAssembler::TrustedImm32(JSValue::CellTag), GPRInfo::regT1);
+#endif
+ stubJit.move(MacroAssembler::TrustedImmPtr(callLinkInfo.get()), GPRInfo::regT2);
+ slowPathCall = stubJit.nearCall();
+
+ stubJit.addPtr(
+ MacroAssembler::TrustedImm32(alignedNumberOfBytesForCall),
+ MacroAssembler::stackPointerRegister);
+ if (kind == CallGetter)
+ stubJit.setupResults(valueRegs);
+
+ done.append(stubJit.jump());
+ returnUndefined.link(&stubJit);
+
+ if (kind == CallGetter)
+ stubJit.moveTrustedValue(jsUndefined(), valueRegs);
+
+ done.link(&stubJit);
+ } else {
+ // getter: EncodedJSValue (*GetValueFunc)(ExecState*, JSObject* slotBase, EncodedJSValue thisValue, PropertyName);
+ // setter: void (*PutValueFunc)(ExecState*, JSObject* base, EncodedJSValue thisObject, EncodedJSValue value);
+#if USE(JSVALUE64)
+ if (kind == CallCustomGetter)
+ stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseForGetGPR, MacroAssembler::TrustedImmPtr(propertyName.impl()));
+ else
+ stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseForGetGPR, valueRegs.gpr());
+#else
+ if (kind == CallCustomGetter)
+ stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseForGetGPR, MacroAssembler::TrustedImm32(JSValue::CellTag), MacroAssembler::TrustedImmPtr(propertyName.impl()));
+ else
+ stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseForGetGPR, MacroAssembler::TrustedImm32(JSValue::CellTag), valueRegs.payloadGPR(), valueRegs.tagGPR());
+#endif
+ stubJit.storePtr(GPRInfo::callFrameRegister, &vm->topCallFrame);
+
+ operationCall = stubJit.call();
+ if (kind == CallCustomGetter)
+ stubJit.setupResults(valueRegs);
+ MacroAssembler::Jump noException = stubJit.emitExceptionCheck(CCallHelpers::InvertedExceptionCheck);
+
+ stubJit.setupArguments(CCallHelpers::TrustedImmPtr(vm), GPRInfo::callFrameRegister);
+ handlerCall = stubJit.call();
+ stubJit.jumpToExceptionHandler();
+
+ noException.link(&stubJit);
+ }
+ }
+ emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
+
+ LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock(), JITCompilationCanFail);
+ if (patchBuffer.didFailToAllocate())
+ return false;
+
+ linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, successLabel, slowCaseLabel);
+ if (kind == CallCustomGetter || kind == CallCustomSetter) {
+ patchBuffer.link(operationCall, custom);
+ patchBuffer.link(handlerCall, lookupExceptionHandler);
+ } else if (kind == CallGetter || kind == CallSetter) {
+ callLinkInfo->setCallLocations(patchBuffer.locationOfNearCall(slowPathCall),
+ patchBuffer.locationOf(addressOfLinkFunctionCheck),
+ patchBuffer.locationOfNearCall(fastPathCall));
+
+ patchBuffer.link(
+ slowPathCall, CodeLocationLabel(vm->getCTIStub(linkCallThunkGenerator).code()));
+ }
+
+ MacroAssemblerCodeRef code = FINALIZE_CODE_FOR(
+ exec->codeBlock(), patchBuffer,
+ ("%s access stub for %s, return point %p",
+ toString(kind), toCString(*exec->codeBlock()).data(),
+ successLabel.executableAddress()));
+
+ if (kind == CallGetter || kind == CallSetter)
+ stubRoutine = adoptRef(new AccessorCallJITStubRoutine(code, *vm, WTF::move(callLinkInfo)));
+ else
+ stubRoutine = createJITStubRoutine(code, *vm, codeBlock->ownerExecutable(), true);
+
+ return true;
+}
+
+enum InlineCacheAction {
+ GiveUpOnCache,
+ RetryCacheLater,
+ AttemptToCache
+};
+
+static InlineCacheAction actionForCell(VM& vm, JSCell* cell)
+{
+ Structure* structure = cell->structure(vm);
+
+ TypeInfo typeInfo = structure->typeInfo();
+ if (typeInfo.prohibitsPropertyCaching())
+ return GiveUpOnCache;
+
+ if (structure->isUncacheableDictionary()) {
+ if (structure->hasBeenFlattenedBefore())
+ return GiveUpOnCache;
+ // Flattening could have changed the offset, so return early for another try.
+ asObject(cell)->flattenDictionaryObject(vm);
+ return RetryCacheLater;
+ }
+
+ if (!structure->propertyAccessesAreCacheable())
+ return GiveUpOnCache;
+
+ return AttemptToCache;
+}
+
+static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
+{
+ if (Options::forceICFailure())
+ return GiveUpOnCache;
+
+ // FIXME: Write a test that proves we need to check for recursion here just
+ // like the interpreter does, then add a check for recursion.
+
+ CodeBlock* codeBlock = exec->codeBlock();
+ VM* vm = &exec->vm();
+
+ if ((isJSArray(baseValue) || isJSString(baseValue)) && propertyName == exec->propertyNames().length) {
+ GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
+#if USE(JSVALUE32_64)
+ GPRReg resultTagGPR = static_cast<GPRReg>(stubInfo.patch.valueTagGPR);
+#endif
+ GPRReg resultGPR = static_cast<GPRReg>(stubInfo.patch.valueGPR);
+
+ MacroAssembler stubJit;
+
+ if (isJSArray(baseValue)) {
+ GPRReg scratchGPR = TempRegisterSet(stubInfo.patch.usedRegisters).getFreeGPR();
+ bool needToRestoreScratch = false;
+
+ if (scratchGPR == InvalidGPRReg) {
+#if USE(JSVALUE64)
+ scratchGPR = AssemblyHelpers::selectScratchGPR(baseGPR, resultGPR);
+#else
+ scratchGPR = AssemblyHelpers::selectScratchGPR(baseGPR, resultGPR, resultTagGPR);
+#endif
+ stubJit.pushToSave(scratchGPR);
+ needToRestoreScratch = true;
+ }
+
+ MacroAssembler::JumpList failureCases;
+
+ stubJit.load8(MacroAssembler::Address(baseGPR, JSCell::indexingTypeOffset()), scratchGPR);
+ failureCases.append(stubJit.branchTest32(MacroAssembler::Zero, scratchGPR, MacroAssembler::TrustedImm32(IsArray)));
+ failureCases.append(stubJit.branchTest32(MacroAssembler::Zero, scratchGPR, MacroAssembler::TrustedImm32(IndexingShapeMask)));
+
+ stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR);
+ stubJit.load32(MacroAssembler::Address(scratchGPR, ArrayStorage::lengthOffset()), scratchGPR);
+ failureCases.append(stubJit.branch32(MacroAssembler::LessThan, scratchGPR, MacroAssembler::TrustedImm32(0)));
+
+ stubJit.move(scratchGPR, resultGPR);
+#if USE(JSVALUE64)
+ stubJit.or64(AssemblyHelpers::TrustedImm64(TagTypeNumber), resultGPR);
+#elif USE(JSVALUE32_64)
+ stubJit.move(AssemblyHelpers::TrustedImm32(JSValue::Int32Tag), resultTagGPR);
+#endif
+
+ MacroAssembler::Jump success, fail;
+
+ emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
+
+ LinkBuffer patchBuffer(*vm, stubJit, codeBlock, JITCompilationCanFail);
+ if (patchBuffer.didFailToAllocate())
+ return GiveUpOnCache;
+
+ linkRestoreScratch(patchBuffer, needToRestoreScratch, stubInfo, success, fail, failureCases);
+
+ stubInfo.stubRoutine = FINALIZE_CODE_FOR_STUB(
+ exec->codeBlock(), patchBuffer,
+ ("GetById array length stub for %s, return point %p",
+ toCString(*exec->codeBlock()).data(), stubInfo.callReturnLocation.labelAtOffset(
+ stubInfo.patch.deltaCallToDone).executableAddress()));
+
+ RepatchBuffer repatchBuffer(codeBlock);
+ replaceWithJump(repatchBuffer, stubInfo, stubInfo.stubRoutine->code().code());
+ repatchCall(repatchBuffer, stubInfo.callReturnLocation, operationGetById);
+
+ return RetryCacheLater;
+ }
+
+ // String.length case
+ MacroAssembler::Jump failure = stubJit.branch8(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::typeInfoTypeOffset()), MacroAssembler::TrustedImm32(StringType));
+
+ stubJit.load32(MacroAssembler::Address(baseGPR, JSString::offsetOfLength()), resultGPR);
+
+#if USE(JSVALUE64)
+ stubJit.or64(AssemblyHelpers::TrustedImm64(TagTypeNumber), resultGPR);
+#elif USE(JSVALUE32_64)
+ stubJit.move(AssemblyHelpers::TrustedImm32(JSValue::Int32Tag), resultTagGPR);
+#endif
+
+ MacroAssembler::Jump success = stubJit.jump();
+
+ LinkBuffer patchBuffer(*vm, stubJit, codeBlock, JITCompilationCanFail);
+ if (patchBuffer.didFailToAllocate())
+ return GiveUpOnCache;
+
+ patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
+ patchBuffer.link(failure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
+
+ stubInfo.stubRoutine = FINALIZE_CODE_FOR_STUB(
+ exec->codeBlock(), patchBuffer,
+ ("GetById string length stub for %s, return point %p",
+ toCString(*exec->codeBlock()).data(), stubInfo.callReturnLocation.labelAtOffset(
+ stubInfo.patch.deltaCallToDone).executableAddress()));
+
+ RepatchBuffer repatchBuffer(codeBlock);
+ replaceWithJump(repatchBuffer, stubInfo, stubInfo.stubRoutine->code().code());
+ repatchCall(repatchBuffer, stubInfo.callReturnLocation, operationGetById);
+
+ return RetryCacheLater;
+ }
+
+ // FIXME: Cache property access for immediates.
+ if (!baseValue.isCell())
+ return GiveUpOnCache;
+
+ if (!slot.isCacheable() && !slot.isUnset())
+ return GiveUpOnCache;
+
+ JSCell* baseCell = baseValue.asCell();
+ Structure* structure = baseCell->structure(*vm);
+
+ InlineCacheAction action = actionForCell(*vm, baseCell);
+ if (action != AttemptToCache)
+ return action;
+
+ // Optimize self access.
+ if (slot.isCacheableValue()
+ && slot.slotBase() == baseValue
+ && !slot.watchpointSet()
+ && MacroAssembler::isCompactPtrAlignedAddressOffset(maxOffsetRelativeToPatchedStorage(slot.cachedOffset()))) {
+ structure->startWatchingPropertyForReplacements(*vm, slot.cachedOffset());
+ repatchByIdSelfAccess(*vm, codeBlock, stubInfo, structure, propertyName, slot.cachedOffset(), operationGetByIdBuildList, true);
+ stubInfo.initGetByIdSelf(*vm, codeBlock->ownerExecutable(), structure);
+ return RetryCacheLater;
+ }
+
+ repatchCall(codeBlock, stubInfo.callReturnLocation, operationGetByIdBuildList);
+ return RetryCacheLater;
+}
+
+void repatchGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
+{
+ GCSafeConcurrentJITLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
+
+ if (tryCacheGetByID(exec, baseValue, propertyName, slot, stubInfo) == GiveUpOnCache)
+ repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, operationGetById);
+}
+
+static void patchJumpToGetByIdStub(CodeBlock* codeBlock, StructureStubInfo& stubInfo, JITStubRoutine* stubRoutine)
+{
+ RELEASE_ASSERT(stubInfo.accessType == access_get_by_id_list);
+ RepatchBuffer repatchBuffer(codeBlock);
+ if (stubInfo.u.getByIdList.list->didSelfPatching()) {
+ repatchBuffer.relink(
+ stubInfo.callReturnLocation.jumpAtOffset(
+ stubInfo.patch.deltaCallToJump),
+ CodeLocationLabel(stubRoutine->code().code()));
+ return;
+ }
+
+ replaceWithJump(repatchBuffer, stubInfo, stubRoutine->code().code());
+}
+
+static InlineCacheAction tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identifier& ident, const PropertySlot& slot, StructureStubInfo& stubInfo)
+{
+ if (!baseValue.isCell()
+ || (!slot.isCacheable() && !slot.isUnset()))
+ return GiveUpOnCache;
+
+ JSCell* baseCell = baseValue.asCell();
+ bool loadTargetFromProxy = false;
+ if (baseCell->type() == PureForwardingProxyType) {
+ baseValue = jsCast<JSProxy*>(baseCell)->target();
+ baseCell = baseValue.asCell();
+ loadTargetFromProxy = true;
+ }
+
+ VM* vm = &exec->vm();
+ CodeBlock* codeBlock = exec->codeBlock();
+
+ InlineCacheAction action = actionForCell(*vm, baseCell);
+ if (action != AttemptToCache)
+ return action;
+
+ Structure* structure = baseCell->structure(*vm);
+ TypeInfo typeInfo = structure->typeInfo();
+
+ if (stubInfo.patch.spillMode == NeedToSpill) {
+ // We cannot do as much inline caching if the registers were not flushed prior to this GetById. In particular,
+ // non-Value cached properties require planting calls, which requires registers to have been flushed. Thus,
+ // if registers were not flushed, don't do non-Value caching.
+ if (!slot.isCacheableValue() && !slot.isUnset())
+ return GiveUpOnCache;
+ }
+
+ PropertyOffset offset = slot.isUnset() ? invalidOffset : slot.cachedOffset();
+
+ ObjectPropertyConditionSet conditionSet;
+ if (slot.isUnset() || slot.slotBase() != baseValue) {
+ if (typeInfo.prohibitsPropertyCaching() || structure->isDictionary())
+ return GiveUpOnCache;
+
+ if (slot.isUnset())
+ conditionSet = generateConditionsForPropertyMiss(*vm, codeBlock->ownerExecutable(), exec, structure, ident.impl());
+ else
+ conditionSet = generateConditionsForPrototypePropertyHit(*vm, codeBlock->ownerExecutable(), exec, structure, slot.slotBase(), ident.impl());
+
+ if (!conditionSet.isValid())
+ return GiveUpOnCache;
+
+ offset = slot.isUnset() ? invalidOffset : conditionSet.slotBaseCondition().offset();
+ }
+
+ PolymorphicGetByIdList* list = PolymorphicGetByIdList::from(stubInfo);
+ if (list->isFull()) {
+ // We need this extra check because of recursion.
+ return GiveUpOnCache;
+ }
+
+ RefPtr<JITStubRoutine> stubRoutine;
+ bool result = generateByIdStub(
+ exec, kindFor(slot), ident, customFor(slot), stubInfo, conditionSet, slot.slotBase(), offset,
+ structure, loadTargetFromProxy, slot.watchpointSet(),
+ stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
+ CodeLocationLabel(list->currentSlowPathTarget(stubInfo)), stubRoutine);
+ if (!result)
+ return GiveUpOnCache;
+
+ GetByIdAccess::AccessType accessType;
+ if (slot.isCacheableValue())
+ accessType = slot.watchpointSet() ? GetByIdAccess::WatchedStub : GetByIdAccess::SimpleStub;
+ else if (slot.isUnset())
+ accessType = GetByIdAccess::SimpleMiss;
+ else if (slot.isCacheableGetter())
+ accessType = GetByIdAccess::Getter;
+ else
+ accessType = GetByIdAccess::CustomGetter;
+
+ list->addAccess(GetByIdAccess(
+ *vm, codeBlock->ownerExecutable(), accessType, stubRoutine, structure,
+ conditionSet));
+
+ patchJumpToGetByIdStub(codeBlock, stubInfo, stubRoutine.get());
+
+ return list->isFull() ? GiveUpOnCache : RetryCacheLater;
+}
+
+void buildGetByIDList(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
+{
+ GCSafeConcurrentJITLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
+
+ if (tryBuildGetByIDList(exec, baseValue, propertyName, slot, stubInfo) == GiveUpOnCache)
+ repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, operationGetById);
+}
+
+static V_JITOperation_ESsiJJI appropriateGenericPutByIdFunction(const PutPropertySlot &slot, PutKind putKind)
+{
+ if (slot.isStrictMode()) {
+ if (putKind == Direct)
+ return operationPutByIdDirectStrict;
+ return operationPutByIdStrict;
+ }
+ if (putKind == Direct)
+ return operationPutByIdDirectNonStrict;
+ return operationPutByIdNonStrict;
+}
+
+static V_JITOperation_ESsiJJI appropriateListBuildingPutByIdFunction(const PutPropertySlot &slot, PutKind putKind)
+{
+ if (slot.isStrictMode()) {
+ if (putKind == Direct)
+ return operationPutByIdDirectStrictBuildList;
+ return operationPutByIdStrictBuildList;
+ }
+ if (putKind == Direct)
+ return operationPutByIdDirectNonStrictBuildList;
+ return operationPutByIdNonStrictBuildList;
+}
+
+static bool emitPutReplaceStub(
+ ExecState* exec,
+ const Identifier&,
+ const PutPropertySlot& slot,
+ StructureStubInfo& stubInfo,
+ Structure* structure,
+ CodeLocationLabel failureLabel,
+ RefPtr<JITStubRoutine>& stubRoutine)
+{
+ VM* vm = &exec->vm();
+ GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
+#if USE(JSVALUE32_64)
+ GPRReg valueTagGPR = static_cast<GPRReg>(stubInfo.patch.valueTagGPR);
+#endif
+ GPRReg valueGPR = static_cast<GPRReg>(stubInfo.patch.valueGPR);
+
+ ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters);
+ allocator.lock(baseGPR);
+#if USE(JSVALUE32_64)
+ allocator.lock(valueTagGPR);
+#endif
+ allocator.lock(valueGPR);
+
+ GPRReg scratchGPR1 = allocator.allocateScratchGPR();
+
+ CCallHelpers stubJit(vm, exec->codeBlock());
+
+ allocator.preserveReusedRegistersByPushing(stubJit);
+
+ MacroAssembler::Jump badStructure = branchStructure(stubJit,
+ MacroAssembler::NotEqual,
+ MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()),
+ structure);
+
+#if USE(JSVALUE64)
+ if (isInlineOffset(slot.cachedOffset()))
+ stubJit.store64(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue)));
+ else {
+ stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR1);
+ stubJit.store64(valueGPR, MacroAssembler::Address(scratchGPR1, offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue)));
+ }
+#elif USE(JSVALUE32_64)
+ if (isInlineOffset(slot.cachedOffset())) {
+ stubJit.store32(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
+ stubJit.store32(valueTagGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
+ } else {
+ stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR1);
+ stubJit.store32(valueGPR, MacroAssembler::Address(scratchGPR1, offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
+ stubJit.store32(valueTagGPR, MacroAssembler::Address(scratchGPR1, offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
+ }
+#endif
+
+ MacroAssembler::Jump success;
+ MacroAssembler::Jump failure;
+
+ if (allocator.didReuseRegisters()) {
+ allocator.restoreReusedRegistersByPopping(stubJit);
+ success = stubJit.jump();
+
+ badStructure.link(&stubJit);
+ allocator.restoreReusedRegistersByPopping(stubJit);
+ failure = stubJit.jump();
+ } else {
+ success = stubJit.jump();
+ failure = badStructure;
+ }
+
+ LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock(), JITCompilationCanFail);
+ if (patchBuffer.didFailToAllocate())
+ return false;
+
+ patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
+ patchBuffer.link(failure, failureLabel);
+
+ stubRoutine = FINALIZE_CODE_FOR_STUB(
+ exec->codeBlock(), patchBuffer,
+ ("PutById replace stub for %s, return point %p",
+ toCString(*exec->codeBlock()).data(), stubInfo.callReturnLocation.labelAtOffset(
+ stubInfo.patch.deltaCallToDone).executableAddress()));
+
+ return true;
+}
+
+static bool emitPutTransitionStub(
+ ExecState* exec, VM* vm, Structure*& structure, const Identifier& ident,
+ const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind,
+ Structure*& oldStructure, ObjectPropertyConditionSet& conditionSet)
+{
+ PropertyName pname(ident);
+ oldStructure = structure;
+ if (!oldStructure->isObject() || oldStructure->isDictionary() || parseIndex(pname))
+ return false;
+
+ PropertyOffset propertyOffset;
+ structure = Structure::addPropertyTransitionToExistingStructureConcurrently(oldStructure, ident.impl(), 0, propertyOffset);
+
+ if (!structure || !structure->isObject() || structure->isDictionary() || !structure->propertyAccessesAreCacheable())
+ return false;
+
+ // Skip optimizing the case where we need a realloc, if we don't have
+ // enough registers to make it happen.
+ if (GPRInfo::numberOfRegisters < 6
+ && oldStructure->outOfLineCapacity() != structure->outOfLineCapacity()
+ && oldStructure->outOfLineCapacity()) {
+ return false;
+ }
+
+ // Skip optimizing the case where we need realloc, and the structure has
+ // indexing storage.
+ // FIXME: We shouldn't skip this! Implement it!
+ // https://bugs.webkit.org/show_bug.cgi?id=130914
+ if (oldStructure->couldHaveIndexingHeader())
+ return false;
+
+ if (putKind == NotDirect) {
+ conditionSet = generateConditionsForPropertySetterMiss(
+ *vm, exec->codeBlock()->ownerExecutable(), exec, structure, ident.impl());
+ if (!conditionSet.isValid())
+ return false;
+ }
+
+ CodeLocationLabel failureLabel = stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase);
+ RefPtr<JITStubRoutine>& stubRoutine = stubInfo.stubRoutine;
+
+ GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
+#if USE(JSVALUE32_64)
+ GPRReg valueTagGPR = static_cast<GPRReg>(stubInfo.patch.valueTagGPR);
+#endif
+ GPRReg valueGPR = static_cast<GPRReg>(stubInfo.patch.valueGPR);
+
+ ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters);
+ allocator.lock(baseGPR);
+#if USE(JSVALUE32_64)
+ allocator.lock(valueTagGPR);
+#endif
+ allocator.lock(valueGPR);
+
+ CCallHelpers stubJit(vm);
+
+ bool needThirdScratch = false;
+ if (structure->outOfLineCapacity() != oldStructure->outOfLineCapacity()
+ && oldStructure->outOfLineCapacity()) {
+ needThirdScratch = true;
+ }
+
+ GPRReg scratchGPR1 = allocator.allocateScratchGPR();
+ ASSERT(scratchGPR1 != baseGPR);
+ ASSERT(scratchGPR1 != valueGPR);
+
+ GPRReg scratchGPR2 = allocator.allocateScratchGPR();
+ ASSERT(scratchGPR2 != baseGPR);
+ ASSERT(scratchGPR2 != valueGPR);
+ ASSERT(scratchGPR2 != scratchGPR1);
+
+ GPRReg scratchGPR3;
+ if (needThirdScratch) {
+ scratchGPR3 = allocator.allocateScratchGPR();
+ ASSERT(scratchGPR3 != baseGPR);
+ ASSERT(scratchGPR3 != valueGPR);
+ ASSERT(scratchGPR3 != scratchGPR1);
+ ASSERT(scratchGPR3 != scratchGPR2);
+ } else
+ scratchGPR3 = InvalidGPRReg;
+
+ allocator.preserveReusedRegistersByPushing(stubJit);
+
+ MacroAssembler::JumpList failureCases;
+
+ ASSERT(oldStructure->transitionWatchpointSetHasBeenInvalidated());
+
+ failureCases.append(branchStructure(stubJit,
+ MacroAssembler::NotEqual,
+ MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()),
+ oldStructure));
+
+ checkObjectPropertyConditions(
+ conditionSet, exec->codeBlock(), stubInfo, stubJit, failureCases, scratchGPR1);
+
+ MacroAssembler::JumpList slowPath;
+
+ bool scratchGPR1HasStorage = false;
+
+ if (structure->outOfLineCapacity() != oldStructure->outOfLineCapacity()) {
+ size_t newSize = structure->outOfLineCapacity() * sizeof(JSValue);
+ CopiedAllocator* copiedAllocator = &vm->heap.storageAllocator();
+
+ if (!oldStructure->outOfLineCapacity()) {
+ stubJit.loadPtr(&copiedAllocator->m_currentRemaining, scratchGPR1);
+ slowPath.append(stubJit.branchSubPtr(MacroAssembler::Signed, MacroAssembler::TrustedImm32(newSize), scratchGPR1));
+ stubJit.storePtr(scratchGPR1, &copiedAllocator->m_currentRemaining);
+ stubJit.negPtr(scratchGPR1);
+ stubJit.addPtr(MacroAssembler::AbsoluteAddress(&copiedAllocator->m_currentPayloadEnd), scratchGPR1);
+ stubJit.addPtr(MacroAssembler::TrustedImm32(sizeof(JSValue)), scratchGPR1);
+ } else {
+ size_t oldSize = oldStructure->outOfLineCapacity() * sizeof(JSValue);
+ ASSERT(newSize > oldSize);
+
+ stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR3);
+ stubJit.loadPtr(&copiedAllocator->m_currentRemaining, scratchGPR1);
+ slowPath.append(stubJit.branchSubPtr(MacroAssembler::Signed, MacroAssembler::TrustedImm32(newSize), scratchGPR1));
+ stubJit.storePtr(scratchGPR1, &copiedAllocator->m_currentRemaining);
+ stubJit.negPtr(scratchGPR1);
+ stubJit.addPtr(MacroAssembler::AbsoluteAddress(&copiedAllocator->m_currentPayloadEnd), scratchGPR1);
+ stubJit.addPtr(MacroAssembler::TrustedImm32(sizeof(JSValue)), scratchGPR1);
+ // We have scratchGPR1 = new storage, scratchGPR3 = old storage, scratchGPR2 = available
+ for (size_t offset = 0; offset < oldSize; offset += sizeof(void*)) {
+ stubJit.loadPtr(MacroAssembler::Address(scratchGPR3, -static_cast<ptrdiff_t>(offset + sizeof(JSValue) + sizeof(void*))), scratchGPR2);
+ stubJit.storePtr(scratchGPR2, MacroAssembler::Address(scratchGPR1, -static_cast<ptrdiff_t>(offset + sizeof(JSValue) + sizeof(void*))));
+ }
+ }
+
+ stubJit.storePtr(scratchGPR1, MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()));
+ scratchGPR1HasStorage = true;
+ }
+
+ ASSERT(oldStructure->typeInfo().type() == structure->typeInfo().type());
+ ASSERT(oldStructure->typeInfo().inlineTypeFlags() == structure->typeInfo().inlineTypeFlags());
+ ASSERT(oldStructure->indexingType() == structure->indexingType());
+#if USE(JSVALUE64)
+ uint32_t val = structure->id();
+#else
+ uint32_t val = reinterpret_cast<uint32_t>(structure->id());
+#endif
+ stubJit.store32(MacroAssembler::TrustedImm32(val), MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()));
+#if USE(JSVALUE64)
+ if (isInlineOffset(slot.cachedOffset()))
+ stubJit.store64(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue)));
+ else {
+ if (!scratchGPR1HasStorage)
+ stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR1);
+ stubJit.store64(valueGPR, MacroAssembler::Address(scratchGPR1, offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue)));
+ }
+#elif USE(JSVALUE32_64)
+ if (isInlineOffset(slot.cachedOffset())) {
+ stubJit.store32(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
+ stubJit.store32(valueTagGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
+ } else {
+ if (!scratchGPR1HasStorage)
+ stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR1);
+ stubJit.store32(valueGPR, MacroAssembler::Address(scratchGPR1, offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
+ stubJit.store32(valueTagGPR, MacroAssembler::Address(scratchGPR1, offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
+ }
+#endif
+
+ ScratchBuffer* scratchBuffer = nullptr;
+
+#if ENABLE(GGC)
+ MacroAssembler::Call callFlushWriteBarrierBuffer;
+ MacroAssembler::Jump ownerIsRememberedOrInEden = stubJit.jumpIfIsRememberedOrInEden(baseGPR);
+ {
+ WriteBarrierBuffer& writeBarrierBuffer = stubJit.vm()->heap.writeBarrierBuffer();
+ stubJit.load32(writeBarrierBuffer.currentIndexAddress(), scratchGPR2);
+ MacroAssembler::Jump needToFlush =
+ stubJit.branch32(MacroAssembler::AboveOrEqual, scratchGPR2, MacroAssembler::TrustedImm32(writeBarrierBuffer.capacity()));
+
+ stubJit.add32(MacroAssembler::TrustedImm32(1), scratchGPR2);
+ stubJit.store32(scratchGPR2, writeBarrierBuffer.currentIndexAddress());
+
+ stubJit.move(MacroAssembler::TrustedImmPtr(writeBarrierBuffer.buffer()), scratchGPR1);
+ // We use an offset of -sizeof(void*) because we already added 1 to scratchGPR2.
+ stubJit.storePtr(baseGPR, MacroAssembler::BaseIndex(scratchGPR1, scratchGPR2, MacroAssembler::ScalePtr, static_cast<int32_t>(-sizeof(void*))));
+
+ MacroAssembler::Jump doneWithBarrier = stubJit.jump();
+ needToFlush.link(&stubJit);
+
+ scratchBuffer = vm->scratchBufferForSize(allocator.desiredScratchBufferSizeForCall());
+ allocator.preserveUsedRegistersToScratchBufferForCall(stubJit, scratchBuffer, scratchGPR2);
+ stubJit.setupArgumentsWithExecState(baseGPR);
+ callFlushWriteBarrierBuffer = stubJit.call();
+ allocator.restoreUsedRegistersFromScratchBufferForCall(stubJit, scratchBuffer, scratchGPR2);
+
+ doneWithBarrier.link(&stubJit);
+ }
+ ownerIsRememberedOrInEden.link(&stubJit);
+#endif
+
+ MacroAssembler::Jump success;
+ MacroAssembler::Jump failure;
+
+ if (allocator.didReuseRegisters()) {
+ allocator.restoreReusedRegistersByPopping(stubJit);
+ success = stubJit.jump();
+
+ failureCases.link(&stubJit);
+ allocator.restoreReusedRegistersByPopping(stubJit);
+ failure = stubJit.jump();
+ } else
+ success = stubJit.jump();
+
+ MacroAssembler::Call operationCall;
+ MacroAssembler::Jump successInSlowPath;
+
+ if (structure->outOfLineCapacity() != oldStructure->outOfLineCapacity()) {
+ slowPath.link(&stubJit);
+
+ allocator.restoreReusedRegistersByPopping(stubJit);
+ if (!scratchBuffer)
+ scratchBuffer = vm->scratchBufferForSize(allocator.desiredScratchBufferSizeForCall());
+ allocator.preserveUsedRegistersToScratchBufferForCall(stubJit, scratchBuffer, scratchGPR1);
+#if USE(JSVALUE64)
+ stubJit.setupArgumentsWithExecState(baseGPR, MacroAssembler::TrustedImmPtr(structure), MacroAssembler::TrustedImm32(slot.cachedOffset()), valueGPR);
+#else
+ stubJit.setupArgumentsWithExecState(baseGPR, MacroAssembler::TrustedImmPtr(structure), MacroAssembler::TrustedImm32(slot.cachedOffset()), valueGPR, valueTagGPR);
+#endif
+ operationCall = stubJit.call();
+ allocator.restoreUsedRegistersFromScratchBufferForCall(stubJit, scratchBuffer, scratchGPR1);
+ successInSlowPath = stubJit.jump();
+ }
+
+ LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock(), JITCompilationCanFail);
+ if (patchBuffer.didFailToAllocate())
+ return false;
+
+ patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
+ if (allocator.didReuseRegisters())
+ patchBuffer.link(failure, failureLabel);
+ else
+ patchBuffer.link(failureCases, failureLabel);
+#if ENABLE(GGC)
+ patchBuffer.link(callFlushWriteBarrierBuffer, operationFlushWriteBarrierBuffer);
+#endif
+ if (structure->outOfLineCapacity() != oldStructure->outOfLineCapacity()) {
+ patchBuffer.link(operationCall, operationReallocateStorageAndFinishPut);
+ patchBuffer.link(successInSlowPath, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
+ }
+
+ stubRoutine =
+ createJITStubRoutine(
+ FINALIZE_CODE_FOR(
+ exec->codeBlock(), patchBuffer,
+ ("PutById %stransition stub (%p -> %p) for %s, return point %p",
+ structure->outOfLineCapacity() != oldStructure->outOfLineCapacity() ? "reallocating " : "",
+ oldStructure, structure,
+ toCString(*exec->codeBlock()).data(), stubInfo.callReturnLocation.labelAtOffset(
+ stubInfo.patch.deltaCallToDone).executableAddress())),
+ *vm,
+ exec->codeBlock()->ownerExecutable(),
+ structure->outOfLineCapacity() != oldStructure->outOfLineCapacity(),
+ structure);
+
+ return true;
+}
+
+static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Structure* structure, const Identifier& ident, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
+{
+ if (Options::forceICFailure())
+ return GiveUpOnCache;
+
+ CodeBlock* codeBlock = exec->codeBlock();
+ VM* vm = &exec->vm();
+
+ if (!baseValue.isCell())
+ return GiveUpOnCache;
+
+ if (!slot.isCacheablePut() && !slot.isCacheableCustom() && !slot.isCacheableSetter())
+ return GiveUpOnCache;
+
+ if (!structure->propertyAccessesAreCacheable())
+ return GiveUpOnCache;
+
+ // Optimize self access.
+ if (slot.base() == baseValue && slot.isCacheablePut()) {
+ if (slot.type() == PutPropertySlot::NewProperty) {
+
+ Structure* oldStructure;
+ ObjectPropertyConditionSet conditionSet;
+ if (!emitPutTransitionStub(exec, vm, structure, ident, slot, stubInfo, putKind, oldStructure, conditionSet))
+ return GiveUpOnCache;
+
+ RepatchBuffer repatchBuffer(codeBlock);
+ repatchBuffer.relink(
+ stubInfo.callReturnLocation.jumpAtOffset(
+ stubInfo.patch.deltaCallToJump),
+ CodeLocationLabel(stubInfo.stubRoutine->code().code()));
+ repatchCall(repatchBuffer, stubInfo.callReturnLocation, appropriateListBuildingPutByIdFunction(slot, putKind));
+
+ stubInfo.initPutByIdTransition(*vm, codeBlock->ownerExecutable(), oldStructure, structure, conditionSet, putKind == Direct);
+
+ return RetryCacheLater;
+ }
+
+ if (!MacroAssembler::isPtrAlignedAddressOffset(offsetRelativeToPatchedStorage(slot.cachedOffset())))
+ return GiveUpOnCache;
+
+ structure->didCachePropertyReplacement(*vm, slot.cachedOffset());
+ repatchByIdSelfAccess(*vm, codeBlock, stubInfo, structure, ident, slot.cachedOffset(), appropriateListBuildingPutByIdFunction(slot, putKind), false);
+ stubInfo.initPutByIdReplace(*vm, codeBlock->ownerExecutable(), structure);
+ return RetryCacheLater;
+ }
+
+ if ((slot.isCacheableCustom() || slot.isCacheableSetter())
+ && stubInfo.patch.spillMode == DontSpill) {
+ RefPtr<JITStubRoutine> stubRoutine;
+
+ ObjectPropertyConditionSet conditionSet;
+ PropertyOffset offset;
+ if (slot.base() != baseValue) {
+ if (slot.isCacheableCustom()) {
+ conditionSet =
+ generateConditionsForPrototypePropertyHitCustom(
+ *vm, codeBlock->ownerExecutable(), exec, structure, slot.base(),
+ ident.impl());
+ } else {
+ conditionSet =
+ generateConditionsForPrototypePropertyHit(
+ *vm, codeBlock->ownerExecutable(), exec, structure, slot.base(),
+ ident.impl());
+ }
+ if (!conditionSet.isValid())
+ return GiveUpOnCache;
+ offset = slot.isCacheableCustom() ? invalidOffset : conditionSet.slotBaseCondition().offset();
+ } else
+ offset = slot.cachedOffset();
+
+ PolymorphicPutByIdList* list;
+ list = PolymorphicPutByIdList::from(putKind, stubInfo);
+
+ bool result = generateByIdStub(
+ exec, kindFor(slot), ident, customFor(slot), stubInfo, conditionSet, slot.base(),
+ offset, structure, false, nullptr,
+ stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
+ stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase),
+ stubRoutine);
+ if (!result)
+ return GiveUpOnCache;
+
+ list->addAccess(PutByIdAccess::setter(
+ *vm, codeBlock->ownerExecutable(),
+ slot.isCacheableSetter() ? PutByIdAccess::Setter : PutByIdAccess::CustomSetter,
+ structure, conditionSet, slot.customSetter(), stubRoutine));
+
+ RepatchBuffer repatchBuffer(codeBlock);
+ repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
+ repatchCall(repatchBuffer, stubInfo.callReturnLocation, appropriateListBuildingPutByIdFunction(slot, putKind));
+ RELEASE_ASSERT(!list->isFull());
+ return RetryCacheLater;
+ }
+
+ return GiveUpOnCache;
+}
+
+void repatchPutByID(ExecState* exec, JSValue baseValue, Structure* structure, const Identifier& propertyName, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
+{
+ GCSafeConcurrentJITLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
+
+ if (tryCachePutByID(exec, baseValue, structure, propertyName, slot, stubInfo, putKind) == GiveUpOnCache)
+ repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind));
+}
+
+static InlineCacheAction tryBuildPutByIdList(ExecState* exec, JSValue baseValue, Structure* structure, const Identifier& propertyName, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
+{
+ CodeBlock* codeBlock = exec->codeBlock();
+ VM* vm = &exec->vm();
+
+ if (!baseValue.isCell())
+ return GiveUpOnCache;
+
+ if (!slot.isCacheablePut() && !slot.isCacheableCustom() && !slot.isCacheableSetter())
+ return GiveUpOnCache;
+
+ if (!structure->propertyAccessesAreCacheable())
+ return GiveUpOnCache;
+
+ // Optimize self access.
+ if (slot.base() == baseValue && slot.isCacheablePut()) {
+ PolymorphicPutByIdList* list;
+ RefPtr<JITStubRoutine> stubRoutine;
+
+ if (slot.type() == PutPropertySlot::NewProperty) {
+ list = PolymorphicPutByIdList::from(putKind, stubInfo);
+ if (list->isFull())
+ return GiveUpOnCache; // Will get here due to recursion.
+
+ Structure* oldStructure;
+ ObjectPropertyConditionSet conditionSet;
+ if (!emitPutTransitionStub(exec, vm, structure, propertyName, slot, stubInfo, putKind, oldStructure, conditionSet))
+ return GiveUpOnCache;
+
+ stubRoutine = stubInfo.stubRoutine;
+ list->addAccess(
+ PutByIdAccess::transition(
+ *vm, codeBlock->ownerExecutable(),
+ oldStructure, structure, conditionSet,
+ stubRoutine));
+
+ } else {
+ list = PolymorphicPutByIdList::from(putKind, stubInfo);
+ if (list->isFull())
+ return GiveUpOnCache; // Will get here due to recursion.
+
+ structure->didCachePropertyReplacement(*vm, slot.cachedOffset());
+
+ // We're now committed to creating the stub. Mogrify the meta-data accordingly.
+ bool result = emitPutReplaceStub(
+ exec, propertyName, slot, stubInfo,
+ structure, CodeLocationLabel(list->currentSlowPathTarget()), stubRoutine);
+ if (!result)
+ return GiveUpOnCache;
+
+ list->addAccess(
+ PutByIdAccess::replace(
+ *vm, codeBlock->ownerExecutable(),
+ structure, stubRoutine));
+ }
+ RepatchBuffer repatchBuffer(codeBlock);
+ repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
+ if (list->isFull())
+ repatchCall(repatchBuffer, stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind));
+
+ return RetryCacheLater;
+ }
+
+ if ((slot.isCacheableCustom() || slot.isCacheableSetter())
+ && stubInfo.patch.spillMode == DontSpill) {
+ RefPtr<JITStubRoutine> stubRoutine;
+
+ ObjectPropertyConditionSet conditionSet;
+ PropertyOffset offset;
+ if (slot.base() != baseValue) {
+ if (slot.isCacheableCustom()) {
+ conditionSet =
+ generateConditionsForPrototypePropertyHitCustom(
+ *vm, codeBlock->ownerExecutable(), exec, structure, slot.base(),
+ propertyName.impl());
+ } else {
+ conditionSet =
+ generateConditionsForPrototypePropertyHit(
+ *vm, codeBlock->ownerExecutable(), exec, structure, slot.base(),
+ propertyName.impl());
+ }
+ if (!conditionSet.isValid())
+ return GiveUpOnCache;
+ offset = slot.isCacheableCustom() ? invalidOffset : conditionSet.slotBaseCondition().offset();
+ } else
+ offset = slot.cachedOffset();
+
+ PolymorphicPutByIdList* list;
+ list = PolymorphicPutByIdList::from(putKind, stubInfo);
+
+ bool result = generateByIdStub(
+ exec, kindFor(slot), propertyName, customFor(slot), stubInfo, conditionSet, slot.base(),
+ offset, structure, false, nullptr,
+ stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
+ CodeLocationLabel(list->currentSlowPathTarget()),
+ stubRoutine);
+ if (!result)
+ return GiveUpOnCache;
+
+ list->addAccess(PutByIdAccess::setter(
+ *vm, codeBlock->ownerExecutable(),
+ slot.isCacheableSetter() ? PutByIdAccess::Setter : PutByIdAccess::CustomSetter,
+ structure, conditionSet, slot.customSetter(), stubRoutine));
+
+ RepatchBuffer repatchBuffer(codeBlock);
+ repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
+ if (list->isFull())
+ repatchCall(repatchBuffer, stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind));
+
+ return RetryCacheLater;
+ }
+ return GiveUpOnCache;
+}
+
+void buildPutByIdList(ExecState* exec, JSValue baseValue, Structure* structure, const Identifier& propertyName, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
+{
+ GCSafeConcurrentJITLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
+
+ if (tryBuildPutByIdList(exec, baseValue, structure, propertyName, slot, stubInfo, putKind) == GiveUpOnCache)
+ repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind));
+}
+
+static InlineCacheAction tryRepatchIn(
+ ExecState* exec, JSCell* base, const Identifier& ident, bool wasFound,
+ const PropertySlot& slot, StructureStubInfo& stubInfo)
+{
+ if (Options::forceICFailure())
+ return GiveUpOnCache;
+
+ if (!base->structure()->propertyAccessesAreCacheable())
+ return GiveUpOnCache;
+
+ if (wasFound) {
+ if (!slot.isCacheable())
+ return GiveUpOnCache;
+ }
+
+ CodeBlock* codeBlock = exec->codeBlock();
+ VM* vm = &exec->vm();
+ Structure* structure = base->structure(*vm);
+
+ ObjectPropertyConditionSet conditionSet;
+ if (wasFound) {
+ if (slot.slotBase() != base) {
+ conditionSet = generateConditionsForPrototypePropertyHit(
+ *vm, codeBlock->ownerExecutable(), exec, structure, slot.slotBase(), ident.impl());
+ }
+ } else {
+ conditionSet = generateConditionsForPropertyMiss(
+ *vm, codeBlock->ownerExecutable(), exec, structure, ident.impl());
+ }
+ if (!conditionSet.isValid())
+ return GiveUpOnCache;
+
+ PolymorphicAccessStructureList* polymorphicStructureList;
+ int listIndex;
+
+ CodeLocationLabel successLabel = stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone);
+ CodeLocationLabel slowCaseLabel;
+
+ if (stubInfo.accessType == access_unset) {
+ polymorphicStructureList = new PolymorphicAccessStructureList();
+ stubInfo.initInList(polymorphicStructureList, 0);
+ slowCaseLabel = stubInfo.callReturnLocation.labelAtOffset(
+ stubInfo.patch.deltaCallToSlowCase);
+ listIndex = 0;
+ } else {
+ RELEASE_ASSERT(stubInfo.accessType == access_in_list);
+ polymorphicStructureList = stubInfo.u.inList.structureList;
+ listIndex = stubInfo.u.inList.listSize;
+ slowCaseLabel = CodeLocationLabel(polymorphicStructureList->list[listIndex - 1].stubRoutine->code().code());
+
+ if (listIndex == POLYMORPHIC_LIST_CACHE_SIZE)
+ return GiveUpOnCache;
+ }
+
+ RefPtr<JITStubRoutine> stubRoutine;
+
+ {
+ GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
+ GPRReg resultGPR = static_cast<GPRReg>(stubInfo.patch.valueGPR);
+ GPRReg scratchGPR = TempRegisterSet(stubInfo.patch.usedRegisters).getFreeGPR();
+
+ CCallHelpers stubJit(vm);
+
+ bool needToRestoreScratch;
+ if (scratchGPR == InvalidGPRReg) {
+ scratchGPR = AssemblyHelpers::selectScratchGPR(baseGPR, resultGPR);
+ stubJit.pushToSave(scratchGPR);
+ needToRestoreScratch = true;
+ } else
+ needToRestoreScratch = false;
+
+ MacroAssembler::JumpList failureCases;
+ failureCases.append(branchStructure(stubJit,
+ MacroAssembler::NotEqual,
+ MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()),
+ structure));
+
+ CodeBlock* codeBlock = exec->codeBlock();
+ if (structure->typeInfo().newImpurePropertyFiresWatchpoints())
+ vm->registerWatchpointForImpureProperty(ident, stubInfo.addWatchpoint(codeBlock));
+
+ if (slot.watchpointSet())
+ slot.watchpointSet()->add(stubInfo.addWatchpoint(codeBlock));
+
+ checkObjectPropertyConditions(
+ conditionSet, exec->codeBlock(), stubInfo, stubJit, failureCases, scratchGPR);
+
+#if USE(JSVALUE64)
+ stubJit.move(MacroAssembler::TrustedImm64(JSValue::encode(jsBoolean(wasFound))), resultGPR);
+#else
+ stubJit.move(MacroAssembler::TrustedImm32(wasFound), resultGPR);
+#endif
+
+ MacroAssembler::Jump success, fail;
+
+ emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
+
+ LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock(), JITCompilationCanFail);
+ if (patchBuffer.didFailToAllocate())
+ return GiveUpOnCache;
+
+ linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, successLabel, slowCaseLabel);
+
+ stubRoutine = FINALIZE_CODE_FOR_STUB(
+ exec->codeBlock(), patchBuffer,
+ ("In (found = %s) stub for %s, return point %p",
+ wasFound ? "yes" : "no", toCString(*exec->codeBlock()).data(),
+ successLabel.executableAddress()));
+ }
+
+ polymorphicStructureList->list[listIndex].set(*vm, codeBlock->ownerExecutable(), stubRoutine, structure, true);
+ stubInfo.u.inList.listSize++;
+
+ RepatchBuffer repatchBuffer(codeBlock);
+ repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
+
+ return listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1) ? RetryCacheLater : GiveUpOnCache;
+}
+
+void repatchIn(
+ ExecState* exec, JSCell* base, const Identifier& ident, bool wasFound,
+ const PropertySlot& slot, StructureStubInfo& stubInfo)
+{
+ if (tryRepatchIn(exec, base, ident, wasFound, slot, stubInfo) == GiveUpOnCache)
+ repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, operationIn);
+}
+
+static void linkSlowFor(
+ RepatchBuffer& repatchBuffer, VM*, CallLinkInfo& callLinkInfo, MacroAssemblerCodeRef codeRef)
+{
+ repatchBuffer.relink(
+ callLinkInfo.callReturnLocation(), codeRef.code());
+}
+
+static void linkSlowFor(
+ RepatchBuffer& repatchBuffer, VM* vm, CallLinkInfo& callLinkInfo, ThunkGenerator generator)
+{
+ linkSlowFor(repatchBuffer, vm, callLinkInfo, vm->getCTIStub(generator));
+}
+
+static void linkSlowFor(
+ RepatchBuffer& repatchBuffer, VM* vm, CallLinkInfo& callLinkInfo)
+{
+ MacroAssemblerCodeRef virtualThunk = virtualThunkFor(vm, callLinkInfo);
+ linkSlowFor(repatchBuffer, vm, callLinkInfo, virtualThunk);
+ callLinkInfo.setSlowStub(createJITStubRoutine(virtualThunk, *vm, nullptr, true));
+}
+
+void linkFor(
+ ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCodeBlock,
+ JSFunction* callee, MacroAssemblerCodePtr codePtr)
+{
+ ASSERT(!callLinkInfo.stub());
+
+ CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
+
+ VM* vm = callerCodeBlock->vm();
+
+ RepatchBuffer repatchBuffer(callerCodeBlock);
+
+ ASSERT(!callLinkInfo.isLinked());
+ callLinkInfo.setCallee(exec->callerFrame()->vm(), callLinkInfo.hotPathBegin(), callerCodeBlock->ownerExecutable(), callee);
+ callLinkInfo.setLastSeenCallee(exec->callerFrame()->vm(), callerCodeBlock->ownerExecutable(), callee);
+ if (shouldShowDisassemblyFor(callerCodeBlock))
+ dataLog("Linking call in ", *callerCodeBlock, " at ", callLinkInfo.codeOrigin(), " to ", pointerDump(calleeCodeBlock), ", entrypoint at ", codePtr, "\n");
+ repatchBuffer.relink(callLinkInfo.hotPathOther(), codePtr);
+
+ if (calleeCodeBlock)
+ calleeCodeBlock->linkIncomingCall(exec->callerFrame(), &callLinkInfo);
+
+ if (callLinkInfo.specializationKind() == CodeForCall) {
+ linkSlowFor(
+ repatchBuffer, vm, callLinkInfo, linkPolymorphicCallThunkGenerator);
+ return;
+ }
+
+ ASSERT(callLinkInfo.specializationKind() == CodeForConstruct);
+ linkSlowFor(repatchBuffer, vm, callLinkInfo);
+}
+
+void linkSlowFor(
+ ExecState* exec, CallLinkInfo& callLinkInfo)
+{
+ CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
+ VM* vm = callerCodeBlock->vm();
+
+ RepatchBuffer repatchBuffer(callerCodeBlock);
+
+ linkSlowFor(repatchBuffer, vm, callLinkInfo);
+}
+
+static void revertCall(
+ RepatchBuffer& repatchBuffer, VM* vm, CallLinkInfo& callLinkInfo, MacroAssemblerCodeRef codeRef)
+{
+ repatchBuffer.revertJumpReplacementToBranchPtrWithPatch(
+ RepatchBuffer::startOfBranchPtrWithPatchOnRegister(callLinkInfo.hotPathBegin()),
+ static_cast<MacroAssembler::RegisterID>(callLinkInfo.calleeGPR()), 0);
+ linkSlowFor(repatchBuffer, vm, callLinkInfo, codeRef);
+ callLinkInfo.clearSeen();
+ callLinkInfo.clearCallee();
+ callLinkInfo.clearStub();
+ callLinkInfo.clearSlowStub();
+ if (callLinkInfo.isOnList())
+ callLinkInfo.remove();
+}
+
+void unlinkFor(
+ RepatchBuffer& repatchBuffer, CallLinkInfo& callLinkInfo)
+{
+ if (Options::showDisassembly())
+ dataLog("Unlinking call from ", callLinkInfo.callReturnLocation(), " in request from ", pointerDump(repatchBuffer.codeBlock()), "\n");
+
+ VM* vm = repatchBuffer.codeBlock()->vm();
+ revertCall(repatchBuffer, vm, callLinkInfo, vm->getCTIStub(linkCallThunkGenerator));
+}
+
+void linkVirtualFor(
+ ExecState* exec, CallLinkInfo& callLinkInfo)
+{
+ CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
+ VM* vm = callerCodeBlock->vm();
+
+ if (shouldShowDisassemblyFor(callerCodeBlock))
+ dataLog("Linking virtual call at ", *callerCodeBlock, " ", exec->callerFrame()->codeOrigin(), "\n");
+
+ RepatchBuffer repatchBuffer(callerCodeBlock);
+ MacroAssemblerCodeRef virtualThunk = virtualThunkFor(vm, callLinkInfo);
+ revertCall(repatchBuffer, vm, callLinkInfo, virtualThunk);
+ callLinkInfo.setSlowStub(createJITStubRoutine(virtualThunk, *vm, nullptr, true));
+}
+
+namespace {
+struct CallToCodePtr {
+ CCallHelpers::Call call;
+ MacroAssemblerCodePtr codePtr;
+};
+} // annonymous namespace
+
+void linkPolymorphicCall(
+ ExecState* exec, CallLinkInfo& callLinkInfo, CallVariant newVariant)
+{
+ // Currently we can't do anything for non-function callees.
+ // https://bugs.webkit.org/show_bug.cgi?id=140685
+ if (!newVariant || !newVariant.executable()) {
+ linkVirtualFor(exec, callLinkInfo);
+ return;
+ }
+
+ CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
+ VM* vm = callerCodeBlock->vm();
+
+ CallVariantList list;
+ if (PolymorphicCallStubRoutine* stub = callLinkInfo.stub())
+ list = stub->variants();
+ else if (JSFunction* oldCallee = callLinkInfo.callee())
+ list = CallVariantList{ CallVariant(oldCallee) };
+
+ list = variantListWithVariant(list, newVariant);
+
+ // If there are any closure calls then it makes sense to treat all of them as closure calls.
+ // This makes switching on callee cheaper. It also produces profiling that's easier on the DFG;
+ // the DFG doesn't really want to deal with a combination of closure and non-closure callees.
+ bool isClosureCall = false;
+ for (CallVariant variant : list) {
+ if (variant.isClosureCall()) {
+ list = despecifiedVariantList(list);
+ isClosureCall = true;
+ break;
+ }
+ }
+
+ if (isClosureCall)
+ callLinkInfo.setHasSeenClosure();
+
+ Vector<PolymorphicCallCase> callCases;
+
+ // Figure out what our cases are.
+ for (CallVariant variant : list) {
+ CodeBlock* codeBlock;
+ if (variant.executable()->isHostFunction())
+ codeBlock = nullptr;
+ else {
+ codeBlock = jsCast<FunctionExecutable*>(variant.executable())->codeBlockForCall();
+
+ // If we cannot handle a callee, assume that it's better for this whole thing to be a
+ // virtual call.
+ if (exec->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters()) || callLinkInfo.callType() == CallLinkInfo::CallVarargs || callLinkInfo.callType() == CallLinkInfo::ConstructVarargs) {
+ linkVirtualFor(exec, callLinkInfo);
+ return;
+ }
+ }
+
+ callCases.append(PolymorphicCallCase(variant, codeBlock));
+ }
+
+ // If we are over the limit, just use a normal virtual call.
+ unsigned maxPolymorphicCallVariantListSize;
+ if (callerCodeBlock->jitType() == JITCode::topTierJIT())
+ maxPolymorphicCallVariantListSize = Options::maxPolymorphicCallVariantListSizeForTopTier();
+ else
+ maxPolymorphicCallVariantListSize = Options::maxPolymorphicCallVariantListSize();
+ if (list.size() > maxPolymorphicCallVariantListSize) {
+ linkVirtualFor(exec, callLinkInfo);
+ return;
+ }
+
+ GPRReg calleeGPR = static_cast<GPRReg>(callLinkInfo.calleeGPR());
+
+ CCallHelpers stubJit(vm, callerCodeBlock);
+
+ CCallHelpers::JumpList slowPath;
+
+ ptrdiff_t offsetToFrame = -sizeof(CallerFrameAndPC);
+
+ if (!ASSERT_DISABLED) {
+ CCallHelpers::Jump okArgumentCount = stubJit.branch32(
+ CCallHelpers::Below, CCallHelpers::Address(CCallHelpers::stackPointerRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ArgumentCount) + offsetToFrame + PayloadOffset), CCallHelpers::TrustedImm32(10000000));
+ stubJit.abortWithReason(RepatchInsaneArgumentCount);
+ okArgumentCount.link(&stubJit);
+ }
+
+ GPRReg scratch = AssemblyHelpers::selectScratchGPR(calleeGPR);
+ GPRReg comparisonValueGPR;
+
+ if (isClosureCall) {
+ // Verify that we have a function and stash the executable in scratch.
+
+#if USE(JSVALUE64)
+ // We can safely clobber everything except the calleeGPR. We can't rely on tagMaskRegister
+ // being set. So we do this the hard way.
+ stubJit.move(MacroAssembler::TrustedImm64(TagMask), scratch);
+ slowPath.append(stubJit.branchTest64(CCallHelpers::NonZero, calleeGPR, scratch));
+#else
+ // We would have already checked that the callee is a cell.
+#endif
+
+ slowPath.append(
+ stubJit.branch8(
+ CCallHelpers::NotEqual,
+ CCallHelpers::Address(calleeGPR, JSCell::typeInfoTypeOffset()),
+ CCallHelpers::TrustedImm32(JSFunctionType)));
+
+ stubJit.loadPtr(
+ CCallHelpers::Address(calleeGPR, JSFunction::offsetOfExecutable()),
+ scratch);
+
+ comparisonValueGPR = scratch;
+ } else
+ comparisonValueGPR = calleeGPR;
+
+ Vector<int64_t> caseValues(callCases.size());
+ Vector<CallToCodePtr> calls(callCases.size());
+ std::unique_ptr<uint32_t[]> fastCounts;
+
+ if (callerCodeBlock->jitType() != JITCode::topTierJIT())
+ fastCounts = std::make_unique<uint32_t[]>(callCases.size());
+
+ for (size_t i = 0; i < callCases.size(); ++i) {
+ if (fastCounts)
+ fastCounts[i] = 0;
+
+ CallVariant variant = callCases[i].variant();
+ int64_t newCaseValue;
+ if (isClosureCall)
+ newCaseValue = bitwise_cast<intptr_t>(variant.executable());
+ else
+ newCaseValue = bitwise_cast<intptr_t>(variant.function());
+
+ if (!ASSERT_DISABLED) {
+ for (size_t j = 0; j < i; ++j) {
+ if (caseValues[j] != newCaseValue)
+ continue;
+
+ dataLog("ERROR: Attempt to add duplicate case value.\n");
+ dataLog("Existing case values: ");
+ CommaPrinter comma;
+ for (size_t k = 0; k < i; ++k)
+ dataLog(comma, caseValues[k]);
+ dataLog("\n");
+ dataLog("Attempting to add: ", newCaseValue, "\n");
+ dataLog("Variant list: ", listDump(callCases), "\n");
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ }
+
+ caseValues[i] = newCaseValue;
+ }
+
+ GPRReg fastCountsBaseGPR =
+ AssemblyHelpers::selectScratchGPR(calleeGPR, comparisonValueGPR, GPRInfo::regT3);
+ stubJit.move(CCallHelpers::TrustedImmPtr(fastCounts.get()), fastCountsBaseGPR);
+
+ BinarySwitch binarySwitch(comparisonValueGPR, caseValues, BinarySwitch::IntPtr);
+ CCallHelpers::JumpList done;
+ while (binarySwitch.advance(stubJit)) {
+ size_t caseIndex = binarySwitch.caseIndex();
+
+ CallVariant variant = callCases[caseIndex].variant();
+
+ ASSERT(variant.executable()->hasJITCodeForCall());
+ MacroAssemblerCodePtr codePtr =
+ variant.executable()->generatedJITCodeForCall()->addressForCall(
+ *vm, variant.executable(), ArityCheckNotRequired, callLinkInfo.registerPreservationMode());
+
+ if (fastCounts) {
+ stubJit.add32(
+ CCallHelpers::TrustedImm32(1),
+ CCallHelpers::Address(fastCountsBaseGPR, caseIndex * sizeof(uint32_t)));
+ }
+ calls[caseIndex].call = stubJit.nearCall();
+ calls[caseIndex].codePtr = codePtr;
+ done.append(stubJit.jump());
+ }
+
+ slowPath.link(&stubJit);
+ binarySwitch.fallThrough().link(&stubJit);
+ stubJit.move(calleeGPR, GPRInfo::regT0);
+#if USE(JSVALUE32_64)
+ stubJit.move(CCallHelpers::TrustedImm32(JSValue::CellTag), GPRInfo::regT1);
+#endif
+ stubJit.move(CCallHelpers::TrustedImmPtr(&callLinkInfo), GPRInfo::regT2);
+ stubJit.move(CCallHelpers::TrustedImmPtr(callLinkInfo.callReturnLocation().executableAddress()), GPRInfo::regT4);
+
+ stubJit.restoreReturnAddressBeforeReturn(GPRInfo::regT4);
+ AssemblyHelpers::Jump slow = stubJit.jump();
+
+ LinkBuffer patchBuffer(*vm, stubJit, callerCodeBlock, JITCompilationCanFail);
+ if (patchBuffer.didFailToAllocate()) {
+ linkVirtualFor(exec, callLinkInfo);
+ return;
+ }
+
+ RELEASE_ASSERT(callCases.size() == calls.size());
+ for (CallToCodePtr callToCodePtr : calls) {
+ patchBuffer.link(
+ callToCodePtr.call, FunctionPtr(callToCodePtr.codePtr.executableAddress()));
+ }
+ if (JITCode::isOptimizingJIT(callerCodeBlock->jitType()))
+ patchBuffer.link(done, callLinkInfo.callReturnLocation().labelAtOffset(0));
+ else
+ patchBuffer.link(done, callLinkInfo.hotPathOther().labelAtOffset(0));
+ patchBuffer.link(slow, CodeLocationLabel(vm->getCTIStub(linkPolymorphicCallThunkGenerator).code()));
+
+ RefPtr<PolymorphicCallStubRoutine> stubRoutine = adoptRef(new PolymorphicCallStubRoutine(
+ FINALIZE_CODE_FOR(
+ callerCodeBlock, patchBuffer,
+ ("Polymorphic call stub for %s, return point %p, targets %s",
+ toCString(*callerCodeBlock).data(), callLinkInfo.callReturnLocation().labelAtOffset(0).executableAddress(),
+ toCString(listDump(callCases)).data())),
+ *vm, callerCodeBlock->ownerExecutable(), exec->callerFrame(), callLinkInfo, callCases,
+ WTF::move(fastCounts)));
+
+ RepatchBuffer repatchBuffer(callerCodeBlock);
+
+ repatchBuffer.replaceWithJump(
+ RepatchBuffer::startOfBranchPtrWithPatchOnRegister(callLinkInfo.hotPathBegin()),
+ CodeLocationLabel(stubRoutine->code().code()));
+ // The original slow path is unreachable on 64-bits, but still
+ // reachable on 32-bits since a non-cell callee will always
+ // trigger the slow path
+ linkSlowFor(repatchBuffer, vm, callLinkInfo);
+
+ // If there had been a previous stub routine, that one will die as soon as the GC runs and sees
+ // that it's no longer on stack.
+ callLinkInfo.setStub(stubRoutine.release());
+
+ // The call link info no longer has a call cache apart from the jump to the polymorphic call
+ // stub.
+ if (callLinkInfo.isOnList())
+ callLinkInfo.remove();
+}
+
+void resetGetByID(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo)
+{
+ repatchCall(repatchBuffer, stubInfo.callReturnLocation, operationGetByIdOptimize);
+ CodeLocationDataLabel32 structureLabel = stubInfo.callReturnLocation.dataLabel32AtOffset(-(intptr_t)stubInfo.patch.deltaCheckImmToCall);
+ if (MacroAssembler::canJumpReplacePatchableBranch32WithPatch()) {
+ repatchBuffer.revertJumpReplacementToPatchableBranch32WithPatch(
+ RepatchBuffer::startOfPatchableBranch32WithPatchOnAddress(structureLabel),
+ MacroAssembler::Address(
+ static_cast<MacroAssembler::RegisterID>(stubInfo.patch.baseGPR),
+ JSCell::structureIDOffset()),
+ static_cast<int32_t>(unusedPointer));
+ }
+ repatchBuffer.repatch(structureLabel, static_cast<int32_t>(unusedPointer));
+#if USE(JSVALUE64)
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.deltaCallToLoadOrStore), 0);
+#else
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.deltaCallToTagLoadOrStore), 0);
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.deltaCallToPayloadLoadOrStore), 0);
+#endif
+ repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
+}
+
+void resetPutByID(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo)
+{
+ V_JITOperation_ESsiJJI unoptimizedFunction = bitwise_cast<V_JITOperation_ESsiJJI>(readCallTarget(repatchBuffer, stubInfo.callReturnLocation).executableAddress());
+ V_JITOperation_ESsiJJI optimizedFunction;
+ if (unoptimizedFunction == operationPutByIdStrict || unoptimizedFunction == operationPutByIdStrictBuildList)
+ optimizedFunction = operationPutByIdStrictOptimize;
+ else if (unoptimizedFunction == operationPutByIdNonStrict || unoptimizedFunction == operationPutByIdNonStrictBuildList)
+ optimizedFunction = operationPutByIdNonStrictOptimize;
+ else if (unoptimizedFunction == operationPutByIdDirectStrict || unoptimizedFunction == operationPutByIdDirectStrictBuildList)
+ optimizedFunction = operationPutByIdDirectStrictOptimize;
+ else {
+ ASSERT(unoptimizedFunction == operationPutByIdDirectNonStrict || unoptimizedFunction == operationPutByIdDirectNonStrictBuildList);
+ optimizedFunction = operationPutByIdDirectNonStrictOptimize;
+ }
+ repatchCall(repatchBuffer, stubInfo.callReturnLocation, optimizedFunction);
+ CodeLocationDataLabel32 structureLabel = stubInfo.callReturnLocation.dataLabel32AtOffset(-(intptr_t)stubInfo.patch.deltaCheckImmToCall);
+ if (MacroAssembler::canJumpReplacePatchableBranch32WithPatch()) {
+ repatchBuffer.revertJumpReplacementToPatchableBranch32WithPatch(
+ RepatchBuffer::startOfPatchableBranch32WithPatchOnAddress(structureLabel),
+ MacroAssembler::Address(
+ static_cast<MacroAssembler::RegisterID>(stubInfo.patch.baseGPR),
+ JSCell::structureIDOffset()),
+ static_cast<int32_t>(unusedPointer));
+ }
+ repatchBuffer.repatch(structureLabel, static_cast<int32_t>(unusedPointer));
+#if USE(JSVALUE64)
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.deltaCallToLoadOrStore), 0);
+#else
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.deltaCallToTagLoadOrStore), 0);
+ repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.deltaCallToPayloadLoadOrStore), 0);
+#endif
+ repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
+}
+
+void resetIn(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo)
+{
+ repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
+}
+
+} // namespace JSC
+
+#endif