From 32761a6cee1d0dee366b885b7b9c777e67885688 Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Sun, 10 Apr 2016 09:28:39 +0000 Subject: webkitgtk-2.4.11 --- .../JavaScriptCore/bytecode/PolymorphicAccess.cpp | 1469 -------------------- 1 file changed, 1469 deletions(-) delete mode 100644 Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp (limited to 'Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp') diff --git a/Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp b/Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp deleted file mode 100644 index 3a59f8db4..000000000 --- a/Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp +++ /dev/null @@ -1,1469 +0,0 @@ -/* - * Copyright (C) 2014, 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 "PolymorphicAccess.h" - -#if ENABLE(JIT) - -#include "BinarySwitch.h" -#include "CCallHelpers.h" -#include "CodeBlock.h" -#include "GetterSetter.h" -#include "Heap.h" -#include "JITOperations.h" -#include "JSCInlines.h" -#include "LinkBuffer.h" -#include "ScratchRegisterAllocator.h" -#include "StructureStubClearingWatchpoint.h" -#include "StructureStubInfo.h" -#include -#include - -namespace JSC { - -static const bool verbose = false; - -Watchpoint* AccessGenerationState::addWatchpoint(const ObjectPropertyCondition& condition) -{ - return WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint( - watchpoints, jit->codeBlock(), stubInfo, condition); -} - -void AccessGenerationState::restoreScratch() -{ - allocator->restoreReusedRegistersByPopping(*jit, preservedReusedRegisterState); -} - -void AccessGenerationState::succeed() -{ - restoreScratch(); - success.append(jit->jump()); -} - -void AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling() -{ - if (!m_calculatedRegistersForCallAndExceptionHandling) { - m_calculatedRegistersForCallAndExceptionHandling = true; - - m_liveRegistersToPreserveAtExceptionHandlingCallSite = jit->codeBlock()->jitCode()->liveRegistersToPreserveAtExceptionHandlingCallSite(jit->codeBlock(), stubInfo->callSiteIndex); - m_needsToRestoreRegistersIfException = m_liveRegistersToPreserveAtExceptionHandlingCallSite.numberOfSetRegisters() > 0; - if (m_needsToRestoreRegistersIfException) - RELEASE_ASSERT(JITCode::isOptimizingJIT(jit->codeBlock()->jitType())); - - m_liveRegistersForCall = RegisterSet(m_liveRegistersToPreserveAtExceptionHandlingCallSite, allocator->usedRegisters()); - m_liveRegistersForCall.exclude(RegisterSet::registersToNotSaveForJSCall()); - } -} - -void AccessGenerationState::preserveLiveRegistersToStackForCall() -{ - unsigned extraStackPadding = 0; - unsigned numberOfStackBytesUsedForRegisterPreservation = ScratchRegisterAllocator::preserveRegistersToStackForCall(*jit, liveRegistersForCall(), extraStackPadding); - if (m_numberOfStackBytesUsedForRegisterPreservation != std::numeric_limits::max()) - RELEASE_ASSERT(numberOfStackBytesUsedForRegisterPreservation == m_numberOfStackBytesUsedForRegisterPreservation); - m_numberOfStackBytesUsedForRegisterPreservation = numberOfStackBytesUsedForRegisterPreservation; -} - -void AccessGenerationState::restoreLiveRegistersFromStackForCall(bool isGetter) -{ - RegisterSet dontRestore; - if (isGetter) { - // This is the result value. We don't want to overwrite the result with what we stored to the stack. - // We sometimes have to store it to the stack just in case we throw an exception and need the original value. - dontRestore.set(valueRegs); - } - restoreLiveRegistersFromStackForCall(dontRestore); -} - -void AccessGenerationState::restoreLiveRegistersFromStackForCallWithThrownException() -{ - // Even if we're a getter, we don't want to ignore the result value like we normally do - // because the getter threw, and therefore, didn't return a value that means anything. - // Instead, we want to restore that register to what it was upon entering the getter - // inline cache. The subtlety here is if the base and the result are the same register, - // and the getter threw, we want OSR exit to see the original base value, not the result - // of the getter call. - RegisterSet dontRestore = liveRegistersForCall(); - // As an optimization here, we only need to restore what is live for exception handling. - // We can construct the dontRestore set to accomplish this goal by having it contain only - // what is live for call but not live for exception handling. By ignoring things that are - // only live at the call but not the exception handler, we will only restore things live - // at the exception handler. - dontRestore.exclude(liveRegistersToPreserveAtExceptionHandlingCallSite()); - restoreLiveRegistersFromStackForCall(dontRestore); -} - -void AccessGenerationState::restoreLiveRegistersFromStackForCall(const RegisterSet& dontRestore) -{ - unsigned extraStackPadding = 0; - ScratchRegisterAllocator::restoreRegistersFromStackForCall(*jit, liveRegistersForCall(), dontRestore, m_numberOfStackBytesUsedForRegisterPreservation, extraStackPadding); -} - -CallSiteIndex AccessGenerationState::callSiteIndexForExceptionHandlingOrOriginal() -{ - RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling); - - if (!m_calculatedCallSiteIndex) { - m_calculatedCallSiteIndex = true; - - if (m_needsToRestoreRegistersIfException) - m_callSiteIndex = jit->codeBlock()->newExceptionHandlingCallSiteIndex(stubInfo->callSiteIndex); - else - m_callSiteIndex = originalCallSiteIndex(); - } - - return m_callSiteIndex; -} - -const HandlerInfo& AccessGenerationState::originalExceptionHandler() const -{ - RELEASE_ASSERT(m_needsToRestoreRegistersIfException); - HandlerInfo* exceptionHandler = jit->codeBlock()->handlerForIndex(stubInfo->callSiteIndex.bits()); - RELEASE_ASSERT(exceptionHandler); - return *exceptionHandler; -} - -CallSiteIndex AccessGenerationState::originalCallSiteIndex() const { return stubInfo->callSiteIndex; } - -AccessCase::AccessCase() -{ -} - -std::unique_ptr AccessCase::get( - VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure, - const ObjectPropertyConditionSet& conditionSet, bool viaProxy, WatchpointSet* additionalSet, - PropertySlot::GetValueFunc customGetter, JSObject* customSlotBase) -{ - std::unique_ptr result(new AccessCase()); - - result->m_type = type; - result->m_offset = offset; - result->m_structure.set(vm, owner, structure); - result->m_conditionSet = conditionSet; - - if (viaProxy || additionalSet || result->doesCalls() || customGetter || customSlotBase) { - result->m_rareData = std::make_unique(); - result->m_rareData->viaProxy = viaProxy; - result->m_rareData->additionalSet = additionalSet; - result->m_rareData->customAccessor.getter = customGetter; - result->m_rareData->customSlotBase.setMayBeNull(vm, owner, customSlotBase); - } - - return result; -} - -std::unique_ptr AccessCase::replace( - VM& vm, JSCell* owner, Structure* structure, PropertyOffset offset) -{ - std::unique_ptr result(new AccessCase()); - - result->m_type = Replace; - result->m_offset = offset; - result->m_structure.set(vm, owner, structure); - - return result; -} - -std::unique_ptr AccessCase::transition( - VM& vm, JSCell* owner, Structure* oldStructure, Structure* newStructure, PropertyOffset offset, - const ObjectPropertyConditionSet& conditionSet) -{ - RELEASE_ASSERT(oldStructure == newStructure->previousID()); - - // 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() != newStructure->outOfLineCapacity() - && oldStructure->outOfLineCapacity()) { - return nullptr; - } - - // 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 nullptr; - - std::unique_ptr result(new AccessCase()); - - result->m_type = Transition; - result->m_offset = offset; - result->m_structure.set(vm, owner, newStructure); - result->m_conditionSet = conditionSet; - - return result; -} - -std::unique_ptr AccessCase::setter( - VM& vm, JSCell* owner, AccessType type, Structure* structure, PropertyOffset offset, - const ObjectPropertyConditionSet& conditionSet, PutPropertySlot::PutValueFunc customSetter, - JSObject* customSlotBase) -{ - std::unique_ptr result(new AccessCase()); - - result->m_type = type; - result->m_offset = offset; - result->m_structure.set(vm, owner, structure); - result->m_conditionSet = conditionSet; - result->m_rareData = std::make_unique(); - result->m_rareData->customAccessor.setter = customSetter; - result->m_rareData->customSlotBase.setMayBeNull(vm, owner, customSlotBase); - - return result; -} - -std::unique_ptr AccessCase::in( - VM& vm, JSCell* owner, AccessType type, Structure* structure, - const ObjectPropertyConditionSet& conditionSet) -{ - std::unique_ptr result(new AccessCase()); - - result->m_type = type; - result->m_structure.set(vm, owner, structure); - result->m_conditionSet = conditionSet; - - return result; -} - -std::unique_ptr AccessCase::getLength(VM&, JSCell*, AccessType type) -{ - std::unique_ptr result(new AccessCase()); - - result->m_type = type; - - return result; -} - -std::unique_ptr AccessCase::getIntrinsic( - VM& vm, JSCell* owner, JSFunction* getter, PropertyOffset offset, - Structure* structure, const ObjectPropertyConditionSet& conditionSet) -{ - std::unique_ptr result(new AccessCase()); - - result->m_type = IntrinsicGetter; - result->m_structure.set(vm, owner, structure); - result->m_conditionSet = conditionSet; - result->m_offset = offset; - - result->m_rareData = std::make_unique(); - result->m_rareData->intrinsicFunction.set(vm, owner, getter); - - return result; -} - -AccessCase::~AccessCase() -{ -} - -std::unique_ptr AccessCase::fromStructureStubInfo( - VM& vm, JSCell* owner, StructureStubInfo& stubInfo) -{ - switch (stubInfo.cacheType) { - case CacheType::GetByIdSelf: - return get( - vm, owner, Load, stubInfo.u.byIdSelf.offset, - stubInfo.u.byIdSelf.baseObjectStructure.get()); - - case CacheType::PutByIdReplace: - return replace( - vm, owner, stubInfo.u.byIdSelf.baseObjectStructure.get(), stubInfo.u.byIdSelf.offset); - - default: - return nullptr; - } -} - -std::unique_ptr AccessCase::clone() const -{ - std::unique_ptr result(new AccessCase()); - result->m_type = m_type; - result->m_offset = m_offset; - result->m_structure = m_structure; - result->m_conditionSet = m_conditionSet; - if (RareData* rareData = m_rareData.get()) { - result->m_rareData = std::make_unique(); - result->m_rareData->viaProxy = rareData->viaProxy; - result->m_rareData->additionalSet = rareData->additionalSet; - // NOTE: We don't copy the callLinkInfo, since that's created during code generation. - result->m_rareData->customAccessor.opaque = rareData->customAccessor.opaque; - result->m_rareData->customSlotBase = rareData->customSlotBase; - result->m_rareData->intrinsicFunction = rareData->intrinsicFunction; - } - return result; -} - -bool AccessCase::guardedByStructureCheck() const -{ - if (viaProxy()) - return false; - - switch (m_type) { - case ArrayLength: - case StringLength: - return false; - default: - return true; - } -} - -JSObject* AccessCase::alternateBase() const -{ - if (customSlotBase()) - return customSlotBase(); - return conditionSet().slotBaseCondition().object(); -} - -bool AccessCase::couldStillSucceed() const -{ - return m_conditionSet.structuresEnsureValidityAssumingImpurePropertyWatchpoint(); -} - -bool AccessCase::canReplace(const AccessCase& other) -{ - // We could do a lot better here, but for now we just do something obvious. - - if (!guardedByStructureCheck() || !other.guardedByStructureCheck()) { - // FIXME: Implement this! - return false; - } - - return structure() == other.structure(); -} - -void AccessCase::dump(PrintStream& out) const -{ - out.print(m_type, ":("); - - CommaPrinter comma; - - if (m_type == Transition) - out.print(comma, "structure = ", pointerDump(structure()), " -> ", pointerDump(newStructure())); - else if (m_structure) - out.print(comma, "structure = ", pointerDump(m_structure.get())); - - if (isValidOffset(m_offset)) - out.print(comma, "offset = ", m_offset); - if (!m_conditionSet.isEmpty()) - out.print(comma, "conditions = ", m_conditionSet); - - if (RareData* rareData = m_rareData.get()) { - if (rareData->viaProxy) - out.print(comma, "viaProxy = ", rareData->viaProxy); - if (rareData->additionalSet) - out.print(comma, "additionalSet = ", RawPointer(rareData->additionalSet.get())); - if (rareData->callLinkInfo) - out.print(comma, "callLinkInfo = ", RawPointer(rareData->callLinkInfo.get())); - if (rareData->customAccessor.opaque) - out.print(comma, "customAccessor = ", RawPointer(rareData->customAccessor.opaque)); - if (rareData->customSlotBase) - out.print(comma, "customSlotBase = ", RawPointer(rareData->customSlotBase.get())); - } - - out.print(")"); -} - -bool AccessCase::visitWeak(VM& vm) const -{ - if (m_structure && !Heap::isMarked(m_structure.get())) - return false; - if (!m_conditionSet.areStillLive()) - return false; - if (m_rareData) { - if (m_rareData->callLinkInfo) - m_rareData->callLinkInfo->visitWeak(vm); - if (m_rareData->customSlotBase && !Heap::isMarked(m_rareData->customSlotBase.get())) - return false; - if (m_rareData->intrinsicFunction && !Heap::isMarked(m_rareData->intrinsicFunction.get())) - return false; - } - return true; -} - -void AccessCase::generateWithGuard( - AccessGenerationState& state, CCallHelpers::JumpList& fallThrough) -{ - CCallHelpers& jit = *state.jit; - - switch (m_type) { - case ArrayLength: { - ASSERT(!viaProxy()); - jit.load8(CCallHelpers::Address(state.baseGPR, JSCell::indexingTypeOffset()), state.scratchGPR); - fallThrough.append( - jit.branchTest32( - CCallHelpers::Zero, state.scratchGPR, CCallHelpers::TrustedImm32(IsArray))); - fallThrough.append( - jit.branchTest32( - CCallHelpers::Zero, state.scratchGPR, CCallHelpers::TrustedImm32(IndexingShapeMask))); - break; - } - - case StringLength: { - ASSERT(!viaProxy()); - fallThrough.append( - jit.branch8( - CCallHelpers::NotEqual, - CCallHelpers::Address(state.baseGPR, JSCell::typeInfoTypeOffset()), - CCallHelpers::TrustedImm32(StringType))); - break; - } - - default: { - if (viaProxy()) { - fallThrough.append( - jit.branch8( - CCallHelpers::NotEqual, - CCallHelpers::Address(state.baseGPR, JSCell::typeInfoTypeOffset()), - CCallHelpers::TrustedImm32(PureForwardingProxyType))); - - jit.loadPtr( - CCallHelpers::Address(state.baseGPR, JSProxy::targetOffset()), - state.scratchGPR); - - fallThrough.append( - jit.branchStructure( - CCallHelpers::NotEqual, - CCallHelpers::Address(state.scratchGPR, JSCell::structureIDOffset()), - structure())); - } else { - fallThrough.append( - jit.branchStructure( - CCallHelpers::NotEqual, - CCallHelpers::Address(state.baseGPR, JSCell::structureIDOffset()), - structure())); - } - break; - } }; - - generate(state); -} - -// EncodedJSValue in JSVALUE32_64 is a 64-bit integer. When being compiled in ARM EABI, it must be aligned on an even-numbered register (r0, r2 or [sp]). -// To prevent the assembler from using wrong registers, let's occupy r1 or r3 with a dummy argument when necessary. -#if (COMPILER_SUPPORTS(EABI) && CPU(ARM)) || CPU(MIPS) -#define EABI_32BIT_DUMMY_ARG CCallHelpers::TrustedImm32(0), -#else -#define EABI_32BIT_DUMMY_ARG -#endif - -void AccessCase::generate(AccessGenerationState& state) -{ - if (verbose) - dataLog("Generating code for: ", *this, "\n"); - - CCallHelpers& jit = *state.jit; - VM& vm = *jit.vm(); - CodeBlock* codeBlock = jit.codeBlock(); - StructureStubInfo& stubInfo = *state.stubInfo; - const Identifier& ident = *state.ident; - JSValueRegs valueRegs = state.valueRegs; - GPRReg baseGPR = state.baseGPR; - GPRReg scratchGPR = state.scratchGPR; - - ASSERT(m_conditionSet.structuresEnsureValidityAssumingImpurePropertyWatchpoint()); - - if ((structure() && structure()->needImpurePropertyWatchpoint()) - || m_conditionSet.needImpurePropertyWatchpoint()) - vm.registerWatchpointForImpureProperty(ident, state.addWatchpoint()); - - if (additionalSet()) - additionalSet()->add(state.addWatchpoint()); - - for (const ObjectPropertyCondition& condition : m_conditionSet) { - Structure* structure = condition.object()->structure(); - - if (condition.isWatchableAssumingImpurePropertyWatchpoint()) { - structure->addTransitionWatchpoint(state.addWatchpoint(condition)); - continue; - } - - if (!condition.structureEnsuresValidityAssumingImpurePropertyWatchpoint(structure)) { - dataLog("This condition is no longer met: ", condition, "\n"); - RELEASE_ASSERT_NOT_REACHED(); - } - - // We will emit code that has a weak reference that isn't otherwise listed anywhere. - state.weakReferences.append(WriteBarrier(vm, codeBlock, structure)); - - jit.move(CCallHelpers::TrustedImmPtr(condition.object()), scratchGPR); - state.failAndRepatch.append( - jit.branchStructure( - CCallHelpers::NotEqual, - CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()), - structure)); - } - - switch (m_type) { - case InHit: - case InMiss: - jit.boxBooleanPayload(m_type == InHit, valueRegs.payloadGPR()); - state.succeed(); - return; - - case Miss: - jit.moveTrustedValue(jsUndefined(), valueRegs); - state.succeed(); - return; - - case Load: - case Getter: - case Setter: - case CustomValueGetter: - case CustomAccessorGetter: - case CustomValueSetter: - case CustomAccessorSetter: { - if (isValidOffset(m_offset)) { - Structure* currStructure; - if (m_conditionSet.isEmpty()) - currStructure = structure(); - else - currStructure = m_conditionSet.slotBaseCondition().object()->structure(); - currStructure->startWatchingPropertyForReplacements(vm, offset()); - } - - GPRReg baseForGetGPR; - if (viaProxy()) { - baseForGetGPR = valueRegs.payloadGPR(); - jit.loadPtr( - CCallHelpers::Address(baseGPR, JSProxy::targetOffset()), - baseForGetGPR); - } else - baseForGetGPR = baseGPR; - - GPRReg baseForAccessGPR; - if (!m_conditionSet.isEmpty()) { - jit.move( - CCallHelpers::TrustedImmPtr(alternateBase()), - scratchGPR); - baseForAccessGPR = scratchGPR; - } else - baseForAccessGPR = baseForGetGPR; - - GPRReg loadedValueGPR = InvalidGPRReg; - if (m_type != CustomValueGetter && m_type != CustomAccessorGetter && m_type != CustomValueSetter && m_type != CustomAccessorSetter) { - if (m_type == Load) - loadedValueGPR = valueRegs.payloadGPR(); - else - loadedValueGPR = scratchGPR; - - GPRReg storageGPR; - if (isInlineOffset(m_offset)) - storageGPR = baseForAccessGPR; - else { - jit.loadPtr( - CCallHelpers::Address(baseForAccessGPR, JSObject::butterflyOffset()), - loadedValueGPR); - jit.removeSpaceBits(loadedValueGPR); - storageGPR = loadedValueGPR; - } - -#if USE(JSVALUE64) - jit.load64( - CCallHelpers::Address(storageGPR, offsetRelativeToBase(m_offset)), loadedValueGPR); -#else - if (m_type == Load) { - jit.load32( - CCallHelpers::Address(storageGPR, offsetRelativeToBase(m_offset) + TagOffset), - valueRegs.tagGPR()); - } - jit.load32( - CCallHelpers::Address(storageGPR, offsetRelativeToBase(m_offset) + PayloadOffset), - loadedValueGPR); -#endif - } - - if (m_type == Load) { - state.succeed(); - return; - } - - // Stuff for custom getters/setters. - CCallHelpers::Call operationCall; - CCallHelpers::Call lookupExceptionHandlerCall; - - // Stuff for JS getters/setters. - CCallHelpers::DataLabelPtr addressOfLinkFunctionCheck; - CCallHelpers::Call fastPathCall; - CCallHelpers::Call slowPathCall; - - CCallHelpers::Jump success; - CCallHelpers::Jump fail; - - // This also does the necessary calculations of whether or not we're an - // exception handling call site. - state.calculateLiveRegistersForCallAndExceptionHandling(); - state.preserveLiveRegistersToStackForCall(); - - // Need to make sure that whenever this call is made in the future, we remember the - // place that we made it from. - jit.store32( - CCallHelpers::TrustedImm32(state.callSiteIndexForExceptionHandlingOrOriginal().bits()), - CCallHelpers::tagFor(static_cast(JSStack::ArgumentCount))); - - if (m_type == Getter || m_type == Setter) { - // 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. - - RELEASE_ASSERT(!m_rareData->callLinkInfo); - m_rareData->callLinkInfo = std::make_unique(); - - // FIXME: If we generated a polymorphic call stub that jumped back to the getter - // stub, which then jumped back to the main code, then we'd have a reachability - // situation that the GC doesn't know about. The GC would ensure that the polymorphic - // call stub stayed alive, and it would ensure that the main code stayed alive, but - // it wouldn't know that the getter stub was alive. Ideally JIT stub routines would - // be GC objects, and then we'd be able to say that the polymorphic call stub has a - // reference to the getter stub. - // https://bugs.webkit.org/show_bug.cgi?id=148914 - m_rareData->callLinkInfo->disallowStubs(); - - m_rareData->callLinkInfo->setUpCall( - CallLinkInfo::Call, stubInfo.codeOrigin, loadedValueGPR); - - CCallHelpers::JumpList done; - - // There is a "this" argument. - unsigned numberOfParameters = 1; - // ... and a value argument if we're calling a setter. - if (m_type == Setter) - numberOfParameters++; - - // Get the accessor; if there ain't one then the result is jsUndefined(). - if (m_type == Setter) { - jit.loadPtr( - CCallHelpers::Address(loadedValueGPR, GetterSetter::offsetOfSetter()), - loadedValueGPR); - } else { - jit.loadPtr( - CCallHelpers::Address(loadedValueGPR, GetterSetter::offsetOfGetter()), - loadedValueGPR); - } - - CCallHelpers::Jump returnUndefined = jit.branchTestPtr( - CCallHelpers::Zero, loadedValueGPR); - - unsigned numberOfRegsForCall = JSStack::CallFrameHeaderSize + numberOfParameters; - - unsigned numberOfBytesForCall = - numberOfRegsForCall * sizeof(Register) + sizeof(CallerFrameAndPC); - - unsigned alignedNumberOfBytesForCall = - WTF::roundUpToMultipleOf(stackAlignmentBytes(), numberOfBytesForCall); - - jit.subPtr( - CCallHelpers::TrustedImm32(alignedNumberOfBytesForCall), - CCallHelpers::stackPointerRegister); - - CCallHelpers::Address calleeFrame = CCallHelpers::Address( - CCallHelpers::stackPointerRegister, - -static_cast(sizeof(CallerFrameAndPC))); - - jit.store32( - CCallHelpers::TrustedImm32(numberOfParameters), - calleeFrame.withOffset(JSStack::ArgumentCount * sizeof(Register) + PayloadOffset)); - - jit.storeCell( - loadedValueGPR, calleeFrame.withOffset(JSStack::Callee * sizeof(Register))); - - jit.storeCell( - baseForGetGPR, - calleeFrame.withOffset(virtualRegisterForArgument(0).offset() * sizeof(Register))); - - if (m_type == Setter) { - jit.storeValue( - valueRegs, - calleeFrame.withOffset( - virtualRegisterForArgument(1).offset() * sizeof(Register))); - } - - CCallHelpers::Jump slowCase = jit.branchPtrWithPatch( - CCallHelpers::NotEqual, loadedValueGPR, addressOfLinkFunctionCheck, - CCallHelpers::TrustedImmPtr(0)); - - fastPathCall = jit.nearCall(); - if (m_type == Getter) - jit.setupResults(valueRegs); - done.append(jit.jump()); - - slowCase.link(&jit); - jit.move(loadedValueGPR, GPRInfo::regT0); -#if USE(JSVALUE32_64) - // We *always* know that the getter/setter, if non-null, is a cell. - jit.move(CCallHelpers::TrustedImm32(JSValue::CellTag), GPRInfo::regT1); -#endif - jit.move(CCallHelpers::TrustedImmPtr(m_rareData->callLinkInfo.get()), GPRInfo::regT2); - slowPathCall = jit.nearCall(); - if (m_type == Getter) - jit.setupResults(valueRegs); - done.append(jit.jump()); - - returnUndefined.link(&jit); - if (m_type == Getter) - jit.moveTrustedValue(jsUndefined(), valueRegs); - - done.link(&jit); - - jit.addPtr(CCallHelpers::TrustedImm32((jit.codeBlock()->stackPointerOffset() * sizeof(Register)) - state.preservedReusedRegisterState.numberOfBytesPreserved - state.numberOfStackBytesUsedForRegisterPreservation()), - GPRInfo::callFrameRegister, CCallHelpers::stackPointerRegister); - state.restoreLiveRegistersFromStackForCall(isGetter()); - - state.callbacks.append( - [=, &vm] (LinkBuffer& linkBuffer) { - m_rareData->callLinkInfo->setCallLocations( - linkBuffer.locationOfNearCall(slowPathCall), - linkBuffer.locationOf(addressOfLinkFunctionCheck), - linkBuffer.locationOfNearCall(fastPathCall)); - - linkBuffer.link( - slowPathCall, - CodeLocationLabel(vm.getCTIStub(linkCallThunkGenerator).code())); - }); - } else { - // Need to make room for the C call so any of our stack spillage isn't overwritten. - // We also need to make room because we may be an inline cache in the FTL and not - // have a JIT call frame. - bool needsToMakeRoomOnStackForCCall = state.numberOfStackBytesUsedForRegisterPreservation() || codeBlock->jitType() == JITCode::FTLJIT; - if (needsToMakeRoomOnStackForCCall) - jit.makeSpaceOnStackForCCall(); - - // getter: EncodedJSValue (*GetValueFunc)(ExecState*, EncodedJSValue thisValue, PropertyName); - // setter: void (*PutValueFunc)(ExecState*, EncodedJSValue thisObject, EncodedJSValue value); - // Custom values are passed the slotBase (the property holder), custom accessors are passed the thisVaule (reciever). - GPRReg baseForCustomValue = m_type == CustomValueGetter || m_type == CustomValueSetter ? baseForAccessGPR : baseForGetGPR; -#if USE(JSVALUE64) - if (m_type == CustomValueGetter || m_type == CustomAccessorGetter) { - jit.setupArgumentsWithExecState( - baseForCustomValue, - CCallHelpers::TrustedImmPtr(ident.impl())); - } else - jit.setupArgumentsWithExecState(baseForCustomValue, valueRegs.gpr()); -#else - if (m_type == CustomValueGetter || m_type == CustomAccessorGetter) { - jit.setupArgumentsWithExecState( - EABI_32BIT_DUMMY_ARG baseForCustomValue, - CCallHelpers::TrustedImm32(JSValue::CellTag), - CCallHelpers::TrustedImmPtr(ident.impl())); - } else { - jit.setupArgumentsWithExecState( - EABI_32BIT_DUMMY_ARG baseForCustomValue, - CCallHelpers::TrustedImm32(JSValue::CellTag), - valueRegs.payloadGPR(), valueRegs.tagGPR()); - } -#endif - jit.storePtr(GPRInfo::callFrameRegister, &vm.topCallFrame); - - operationCall = jit.call(); - if (m_type == CustomValueGetter || m_type == CustomAccessorGetter) - jit.setupResults(valueRegs); - if (needsToMakeRoomOnStackForCCall) - jit.reclaimSpaceOnStackForCCall(); - - CCallHelpers::Jump noException = - jit.emitExceptionCheck(CCallHelpers::InvertedExceptionCheck); - - bool didSetLookupExceptionHandler = false; - state.restoreLiveRegistersFromStackForCallWithThrownException(); - state.restoreScratch(); - jit.copyCalleeSavesToVMCalleeSavesBuffer(); - if (state.needsToRestoreRegistersIfException()) { - // To the JIT that produces the original exception handling - // call site, they will expect the OSR exit to be arrived - // at from genericUnwind. Therefore we must model what genericUnwind - // does here. I.e, set callFrameForCatch and copy callee saves. - - jit.storePtr(GPRInfo::callFrameRegister, vm.addressOfCallFrameForCatch()); - CCallHelpers::Jump jumpToOSRExitExceptionHandler = jit.jump(); - - // We don't need to insert a new exception handler in the table - // because we're doing a manual exception check here. i.e, we'll - // never arrive here from genericUnwind(). - HandlerInfo originalHandler = state.originalExceptionHandler(); - state.callbacks.append( - [=] (LinkBuffer& linkBuffer) { - linkBuffer.link(jumpToOSRExitExceptionHandler, originalHandler.nativeCode); - }); - } else { - jit.setupArguments(CCallHelpers::TrustedImmPtr(&vm), GPRInfo::callFrameRegister); - lookupExceptionHandlerCall = jit.call(); - didSetLookupExceptionHandler = true; - jit.jumpToExceptionHandler(); - } - - noException.link(&jit); - state.restoreLiveRegistersFromStackForCall(isGetter()); - - state.callbacks.append( - [=] (LinkBuffer& linkBuffer) { - linkBuffer.link(operationCall, FunctionPtr(m_rareData->customAccessor.opaque)); - if (didSetLookupExceptionHandler) - linkBuffer.link(lookupExceptionHandlerCall, lookupExceptionHandler); - }); - } - state.succeed(); - return; - } - - case Replace: { - if (InferredType* type = structure()->inferredTypeFor(ident.impl())) { - if (verbose) - dataLog("Have type: ", type->descriptor(), "\n"); - state.failAndRepatch.append( - jit.branchIfNotType( - valueRegs, scratchGPR, type->descriptor(), CCallHelpers::DoNotHaveTagRegisters)); - } else if (verbose) - dataLog("Don't have type.\n"); - - if (isInlineOffset(m_offset)) { - jit.storeValue( - valueRegs, - CCallHelpers::Address( - baseGPR, - JSObject::offsetOfInlineStorage() + - offsetInInlineStorage(m_offset) * sizeof(JSValue))); - } else { - jit.loadPtr(CCallHelpers::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR); - state.failAndIgnore.append(jit.branchIfNotToSpace(scratchGPR)); - jit.storeValue( - valueRegs, - CCallHelpers::Address( - scratchGPR, offsetInButterfly(m_offset) * sizeof(JSValue))); - } - state.succeed(); - return; - } - - case Transition: { - // AccessCase::transition() should have returned null. - RELEASE_ASSERT(GPRInfo::numberOfRegisters >= 6 || !structure()->outOfLineCapacity() || structure()->outOfLineCapacity() == newStructure()->outOfLineCapacity()); - RELEASE_ASSERT(!structure()->couldHaveIndexingHeader()); - - if (InferredType* type = newStructure()->inferredTypeFor(ident.impl())) { - if (verbose) - dataLog("Have type: ", type->descriptor(), "\n"); - state.failAndRepatch.append( - jit.branchIfNotType( - valueRegs, scratchGPR, type->descriptor(), CCallHelpers::DoNotHaveTagRegisters)); - } else if (verbose) - dataLog("Don't have type.\n"); - - CCallHelpers::JumpList slowPath; - - ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters); - allocator.lock(baseGPR); -#if USE(JSVALUE32_64) - allocator.lock(static_cast(stubInfo.patch.baseTagGPR)); -#endif - allocator.lock(valueRegs); - allocator.lock(scratchGPR); - - GPRReg scratchGPR2 = allocator.allocateScratchGPR(); - GPRReg scratchGPR3; - if (newStructure()->outOfLineCapacity() != structure()->outOfLineCapacity() - && structure()->outOfLineCapacity()) - scratchGPR3 = allocator.allocateScratchGPR(); - else - scratchGPR3 = InvalidGPRReg; - - ScratchRegisterAllocator::PreservedState preservedState = - allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::SpaceForCCall); - - ASSERT(structure()->transitionWatchpointSetHasBeenInvalidated()); - - bool scratchGPRHasStorage = false; - bool needsToMakeRoomOnStackForCCall = !preservedState.numberOfBytesPreserved && codeBlock->jitType() == JITCode::FTLJIT; - - if (newStructure()->outOfLineCapacity() != structure()->outOfLineCapacity()) { - size_t newSize = newStructure()->outOfLineCapacity() * sizeof(JSValue); - CopiedAllocator* copiedAllocator = &vm.heap.storageAllocator(); - - if (!structure()->outOfLineCapacity()) { - jit.loadPtr(&copiedAllocator->m_currentRemaining, scratchGPR); - slowPath.append( - jit.branchSubPtr( - CCallHelpers::Signed, CCallHelpers::TrustedImm32(newSize), scratchGPR)); - jit.storePtr(scratchGPR, &copiedAllocator->m_currentRemaining); - jit.negPtr(scratchGPR); - jit.addPtr( - CCallHelpers::AbsoluteAddress(&copiedAllocator->m_currentPayloadEnd), scratchGPR); - jit.addPtr(CCallHelpers::TrustedImm32(sizeof(JSValue)), scratchGPR); - } else { - size_t oldSize = structure()->outOfLineCapacity() * sizeof(JSValue); - ASSERT(newSize > oldSize); - - jit.loadPtr(CCallHelpers::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR3); - slowPath.append(jit.branchIfNotToSpace(scratchGPR3)); - jit.loadPtr(&copiedAllocator->m_currentRemaining, scratchGPR); - slowPath.append( - jit.branchSubPtr( - CCallHelpers::Signed, CCallHelpers::TrustedImm32(newSize), scratchGPR)); - jit.storePtr(scratchGPR, &copiedAllocator->m_currentRemaining); - jit.negPtr(scratchGPR); - jit.addPtr( - CCallHelpers::AbsoluteAddress(&copiedAllocator->m_currentPayloadEnd), scratchGPR); - jit.addPtr(CCallHelpers::TrustedImm32(sizeof(JSValue)), scratchGPR); - // We have scratchGPR = new storage, scratchGPR3 = old storage, - // scratchGPR2 = available - for (size_t offset = 0; offset < oldSize; offset += sizeof(void*)) { - jit.loadPtr( - CCallHelpers::Address( - scratchGPR3, - -static_cast( - offset + sizeof(JSValue) + sizeof(void*))), - scratchGPR2); - jit.storePtr( - scratchGPR2, - CCallHelpers::Address( - scratchGPR, - -static_cast(offset + sizeof(JSValue) + sizeof(void*)))); - } - } - - jit.storePtr(scratchGPR, CCallHelpers::Address(baseGPR, JSObject::butterflyOffset())); - scratchGPRHasStorage = true; - } - - uint32_t structureBits = bitwise_cast(newStructure()->id()); - jit.store32( - CCallHelpers::TrustedImm32(structureBits), - CCallHelpers::Address(baseGPR, JSCell::structureIDOffset())); - - if (isInlineOffset(m_offset)) { - jit.storeValue( - valueRegs, - CCallHelpers::Address( - baseGPR, - JSObject::offsetOfInlineStorage() + - offsetInInlineStorage(m_offset) * sizeof(JSValue))); - } else { - if (!scratchGPRHasStorage) { - jit.loadPtr(CCallHelpers::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR); - state.failAndIgnore.append(jit.branchIfNotToSpace(scratchGPR)); - } - jit.storeValue( - valueRegs, - CCallHelpers::Address(scratchGPR, offsetInButterfly(m_offset) * sizeof(JSValue))); - } - - ScratchBuffer* scratchBuffer = nullptr; - if (newStructure()->outOfLineCapacity() != structure()->outOfLineCapacity()) - scratchBuffer = vm.scratchBufferForSize(allocator.desiredScratchBufferSizeForCall()); - - if (newStructure()->outOfLineCapacity() != structure()->outOfLineCapacity()) { - CCallHelpers::Call callFlushWriteBarrierBuffer; - CCallHelpers::Jump ownerIsRememberedOrInEden = jit.jumpIfIsRememberedOrInEden(baseGPR); - WriteBarrierBuffer& writeBarrierBuffer = jit.vm()->heap.writeBarrierBuffer(); - jit.load32(writeBarrierBuffer.currentIndexAddress(), scratchGPR2); - CCallHelpers::Jump needToFlush = - jit.branch32( - CCallHelpers::AboveOrEqual, scratchGPR2, - CCallHelpers::TrustedImm32(writeBarrierBuffer.capacity())); - - jit.add32(CCallHelpers::TrustedImm32(1), scratchGPR2); - jit.store32(scratchGPR2, writeBarrierBuffer.currentIndexAddress()); - - jit.move(CCallHelpers::TrustedImmPtr(writeBarrierBuffer.buffer()), scratchGPR); - // We use an offset of -sizeof(void*) because we already added 1 to scratchGPR2. - jit.storePtr( - baseGPR, - CCallHelpers::BaseIndex( - scratchGPR, scratchGPR2, CCallHelpers::ScalePtr, - static_cast(-sizeof(void*)))); - - CCallHelpers::Jump doneWithBarrier = jit.jump(); - needToFlush.link(&jit); - - // FIXME: We should restoreReusedRegistersByPopping() before this. Then, we wouldn't need - // padding in preserveReusedRegistersByPushing(). Or, maybe it would be even better if the - // barrier slow path was just the normal slow path, below. - // https://bugs.webkit.org/show_bug.cgi?id=149030 - allocator.preserveUsedRegistersToScratchBufferForCall(jit, scratchBuffer, scratchGPR2); - if (needsToMakeRoomOnStackForCCall) - jit.makeSpaceOnStackForCCall(); - jit.setupArgumentsWithExecState(baseGPR); - callFlushWriteBarrierBuffer = jit.call(); - if (needsToMakeRoomOnStackForCCall) - jit.reclaimSpaceOnStackForCCall(); - allocator.restoreUsedRegistersFromScratchBufferForCall( - jit, scratchBuffer, scratchGPR2); - - doneWithBarrier.link(&jit); - ownerIsRememberedOrInEden.link(&jit); - - state.callbacks.append( - [=] (LinkBuffer& linkBuffer) { - linkBuffer.link(callFlushWriteBarrierBuffer, operationFlushWriteBarrierBuffer); - }); - } - - allocator.restoreReusedRegistersByPopping(jit, preservedState); - state.succeed(); - - if (newStructure()->outOfLineCapacity() != structure()->outOfLineCapacity()) { - slowPath.link(&jit); - allocator.restoreReusedRegistersByPopping(jit, preservedState); - allocator.preserveUsedRegistersToScratchBufferForCall(jit, scratchBuffer, scratchGPR); - if (needsToMakeRoomOnStackForCCall) - jit.makeSpaceOnStackForCCall(); -#if USE(JSVALUE64) - jit.setupArgumentsWithExecState( - baseGPR, - CCallHelpers::TrustedImmPtr(newStructure()), - CCallHelpers::TrustedImm32(m_offset), - valueRegs.gpr()); -#else - jit.setupArgumentsWithExecState( - baseGPR, - CCallHelpers::TrustedImmPtr(newStructure()), - CCallHelpers::TrustedImm32(m_offset), - valueRegs.payloadGPR(), valueRegs.tagGPR()); -#endif - CCallHelpers::Call operationCall = jit.call(); - if (needsToMakeRoomOnStackForCCall) - jit.reclaimSpaceOnStackForCCall(); - allocator.restoreUsedRegistersFromScratchBufferForCall(jit, scratchBuffer, scratchGPR); - state.succeed(); - - state.callbacks.append( - [=] (LinkBuffer& linkBuffer) { - linkBuffer.link(operationCall, operationReallocateStorageAndFinishPut); - }); - } - return; - } - - case ArrayLength: { - jit.loadPtr(CCallHelpers::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR); - jit.removeSpaceBits(scratchGPR); - jit.load32(CCallHelpers::Address(scratchGPR, ArrayStorage::lengthOffset()), scratchGPR); - state.failAndIgnore.append( - jit.branch32(CCallHelpers::LessThan, scratchGPR, CCallHelpers::TrustedImm32(0))); - jit.boxInt32(scratchGPR, valueRegs, CCallHelpers::DoNotHaveTagRegisters); - state.succeed(); - return; - } - - case StringLength: { - jit.load32(CCallHelpers::Address(baseGPR, JSString::offsetOfLength()), valueRegs.payloadGPR()); - jit.boxInt32(valueRegs.payloadGPR(), valueRegs, CCallHelpers::DoNotHaveTagRegisters); - state.succeed(); - return; - } - - case IntrinsicGetter: { - RELEASE_ASSERT(isValidOffset(offset())); - - // We need to ensure the getter value does not move from under us. Note that GetterSetters - // are immutable so we just need to watch the property not any value inside it. - Structure* currStructure; - if (m_conditionSet.isEmpty()) - currStructure = structure(); - else - currStructure = m_conditionSet.slotBaseCondition().object()->structure(); - currStructure->startWatchingPropertyForReplacements(vm, offset()); - - emitIntrinsicGetter(state); - return; - } } - - RELEASE_ASSERT_NOT_REACHED(); -} - -PolymorphicAccess::PolymorphicAccess() { } -PolymorphicAccess::~PolymorphicAccess() { } - -MacroAssemblerCodePtr PolymorphicAccess::regenerateWithCases( - VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo, const Identifier& ident, - Vector> originalCasesToAdd) -{ - // This method will add the originalCasesToAdd to the list one at a time while preserving the - // invariants: - // - If a newly added case canReplace() any existing case, then the existing case is removed before - // the new case is added. Removal doesn't change order of the list. Any number of existing cases - // can be removed via the canReplace() rule. - // - Cases in the list always appear in ascending order of time of addition. Therefore, if you - // cascade through the cases in reverse order, you will get the most recent cases first. - // - If this method fails (returns null, doesn't add the cases), then both the previous case list - // and the previous stub are kept intact and the new cases are destroyed. It's OK to attempt to - // add more things after failure. - - // First, verify that we can generate code for all of the new cases while eliminating any of the - // new cases that replace each other. - Vector> casesToAdd; - for (unsigned i = 0; i < originalCasesToAdd.size(); ++i) { - std::unique_ptr myCase = WTFMove(originalCasesToAdd[i]); - - // Add it only if it is not replaced by the subsequent cases in the list. - bool found = false; - for (unsigned j = i + 1; j < originalCasesToAdd.size(); ++j) { - if (originalCasesToAdd[j]->canReplace(*myCase)) { - found = true; - break; - } - } - - if (found) - continue; - - casesToAdd.append(WTFMove(myCase)); - } - - if (verbose) - dataLog("casesToAdd: ", listDump(casesToAdd), "\n"); - - // If there aren't any cases to add, then fail on the grounds that there's no point to generating a - // new stub that will be identical to the old one. Returning null should tell the caller to just - // keep doing what they were doing before. - if (casesToAdd.isEmpty()) - return MacroAssemblerCodePtr(); - - // Now construct the list of cases as they should appear if we are successful. This means putting - // all of the previous cases in this list in order but excluding those that can be replaced, and - // then adding the new cases. - ListType newCases; - for (auto& oldCase : m_list) { - // Ignore old cases that cannot possibly succeed anymore. - if (!oldCase->couldStillSucceed()) - continue; - - // Figure out if this is replaced by any new cases. - bool found = false; - for (auto& caseToAdd : casesToAdd) { - if (caseToAdd->canReplace(*oldCase)) { - found = true; - break; - } - } - if (found) - continue; - - newCases.append(oldCase->clone()); - } - for (auto& caseToAdd : casesToAdd) - newCases.append(WTFMove(caseToAdd)); - - if (verbose) - dataLog("newCases: ", listDump(newCases), "\n"); - - if (newCases.size() > Options::maxAccessVariantListSize()) { - if (verbose) - dataLog("Too many cases.\n"); - return MacroAssemblerCodePtr(); - } - - MacroAssemblerCodePtr result = regenerate(vm, codeBlock, stubInfo, ident, newCases); - if (!result) - return MacroAssemblerCodePtr(); - - m_list = WTFMove(newCases); - return result; -} - -MacroAssemblerCodePtr PolymorphicAccess::regenerateWithCase( - VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo, const Identifier& ident, - std::unique_ptr newAccess) -{ - Vector> newAccesses; - newAccesses.append(WTFMove(newAccess)); - return regenerateWithCases(vm, codeBlock, stubInfo, ident, WTFMove(newAccesses)); -} - -bool PolymorphicAccess::visitWeak(VM& vm) const -{ - for (unsigned i = 0; i < size(); ++i) { - if (!at(i).visitWeak(vm)) - return false; - } - if (Vector>* weakReferences = m_weakReferences.get()) { - for (WriteBarrier& weakReference : *weakReferences) { - if (!Heap::isMarked(weakReference.get())) - return false; - } - } - return true; -} - -void PolymorphicAccess::dump(PrintStream& out) const -{ - out.print(RawPointer(this), ":["); - CommaPrinter comma; - for (auto& entry : m_list) - out.print(comma, *entry); - out.print("]"); -} - -MacroAssemblerCodePtr PolymorphicAccess::regenerate( - VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo, const Identifier& ident, - PolymorphicAccess::ListType& cases) -{ - if (verbose) - dataLog("Generating code for cases: ", listDump(cases), "\n"); - - AccessGenerationState state; - - state.access = this; - state.stubInfo = &stubInfo; - state.ident = &ident; - - state.baseGPR = static_cast(stubInfo.patch.baseGPR); - state.valueRegs = JSValueRegs( -#if USE(JSVALUE32_64) - static_cast(stubInfo.patch.valueTagGPR), -#endif - static_cast(stubInfo.patch.valueGPR)); - - ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters); - state.allocator = &allocator; - allocator.lock(state.baseGPR); - allocator.lock(state.valueRegs); -#if USE(JSVALUE32_64) - allocator.lock(static_cast(stubInfo.patch.baseTagGPR)); -#endif - - state.scratchGPR = allocator.allocateScratchGPR(); - - CCallHelpers jit(&vm, codeBlock); - state.jit = &jit; - - state.preservedReusedRegisterState = - allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::NoExtraSpace); - - bool allGuardedByStructureCheck = true; - bool hasJSGetterSetterCall = false; - for (auto& entry : cases) { - allGuardedByStructureCheck &= entry->guardedByStructureCheck(); - if (entry->type() == AccessCase::Getter || entry->type() == AccessCase::Setter) - hasJSGetterSetterCall = true; - } - - if (cases.isEmpty()) { - // This is super unlikely, but we make it legal anyway. - state.failAndRepatch.append(jit.jump()); - } else if (!allGuardedByStructureCheck || cases.size() == 1) { - // If there are any proxies in the list, we cannot just use a binary switch over the structure. - // We need to resort to a cascade. A cascade also happens to be optimal if we only have just - // one case. - CCallHelpers::JumpList fallThrough; - - // Cascade through the list, preferring newer entries. - for (unsigned i = cases.size(); i--;) { - fallThrough.link(&jit); - cases[i]->generateWithGuard(state, fallThrough); - } - state.failAndRepatch.append(fallThrough); - } else { - jit.load32( - CCallHelpers::Address(state.baseGPR, JSCell::structureIDOffset()), - state.scratchGPR); - - Vector caseValues(cases.size()); - for (unsigned i = 0; i < cases.size(); ++i) - caseValues[i] = bitwise_cast(cases[i]->structure()->id()); - - BinarySwitch binarySwitch(state.scratchGPR, caseValues, BinarySwitch::Int32); - while (binarySwitch.advance(jit)) - cases[binarySwitch.caseIndex()]->generate(state); - state.failAndRepatch.append(binarySwitch.fallThrough()); - } - - if (!state.failAndIgnore.empty()) { - state.failAndIgnore.link(&jit); - - // Make sure that the inline cache optimization code knows that we are taking slow path because - // of something that isn't patchable. The slow path will decrement "countdown" and will only - // patch things if the countdown reaches zero. We increment the slow path count here to ensure - // that the slow path does not try to patch. - jit.load8(&stubInfo.countdown, state.scratchGPR); - jit.add32(CCallHelpers::TrustedImm32(1), state.scratchGPR); - jit.store8(state.scratchGPR, &stubInfo.countdown); - } - - CCallHelpers::JumpList failure; - if (allocator.didReuseRegisters()) { - state.failAndRepatch.link(&jit); - state.restoreScratch(); - } else - failure = state.failAndRepatch; - failure.append(jit.jump()); - - CodeBlock* codeBlockThatOwnsExceptionHandlers = nullptr; - CallSiteIndex callSiteIndexForExceptionHandling; - if (state.needsToRestoreRegistersIfException() && hasJSGetterSetterCall) { - // Emit the exception handler. - // Note that this code is only reachable when doing genericUnwind from a pure JS getter/setter . - // Note also that this is not reachable from custom getter/setter. Custom getter/setters will have - // their own exception handling logic that doesn't go through genericUnwind. - MacroAssembler::Label makeshiftCatchHandler = jit.label(); - - int stackPointerOffset = codeBlock->stackPointerOffset() * sizeof(EncodedJSValue); - stackPointerOffset -= state.preservedReusedRegisterState.numberOfBytesPreserved; - stackPointerOffset -= state.numberOfStackBytesUsedForRegisterPreservation(); - - jit.loadPtr(vm.addressOfCallFrameForCatch(), GPRInfo::callFrameRegister); - jit.addPtr(CCallHelpers::TrustedImm32(stackPointerOffset), GPRInfo::callFrameRegister, CCallHelpers::stackPointerRegister); - - state.restoreLiveRegistersFromStackForCallWithThrownException(); - state.restoreScratch(); - CCallHelpers::Jump jumpToOSRExitExceptionHandler = jit.jump(); - - HandlerInfo oldHandler = state.originalExceptionHandler(); - CallSiteIndex newExceptionHandlingCallSite = state.callSiteIndexForExceptionHandling(); - state.callbacks.append( - [=] (LinkBuffer& linkBuffer) { - linkBuffer.link(jumpToOSRExitExceptionHandler, oldHandler.nativeCode); - - HandlerInfo handlerToRegister = oldHandler; - handlerToRegister.nativeCode = linkBuffer.locationOf(makeshiftCatchHandler); - handlerToRegister.start = newExceptionHandlingCallSite.bits(); - handlerToRegister.end = newExceptionHandlingCallSite.bits() + 1; - codeBlock->appendExceptionHandler(handlerToRegister); - }); - - // We set these to indicate to the stub to remove itself from the CodeBlock's - // exception handler table when it is deallocated. - codeBlockThatOwnsExceptionHandlers = codeBlock; - ASSERT(JITCode::isOptimizingJIT(codeBlockThatOwnsExceptionHandlers->jitType())); - callSiteIndexForExceptionHandling = state.callSiteIndexForExceptionHandling(); - } - - LinkBuffer linkBuffer(vm, jit, codeBlock, JITCompilationCanFail); - if (linkBuffer.didFailToAllocate()) { - if (verbose) - dataLog("Did fail to allocate.\n"); - return MacroAssemblerCodePtr(); - } - - CodeLocationLabel successLabel = - stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone); - - linkBuffer.link(state.success, successLabel); - - linkBuffer.link( - failure, - stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase)); - - for (auto callback : state.callbacks) - callback(linkBuffer); - - if (verbose) - dataLog(*codeBlock, " ", stubInfo.codeOrigin, ": Generating polymorphic access stub for ", listDump(cases), "\n"); - - MacroAssemblerCodeRef code = FINALIZE_CODE_FOR( - codeBlock, linkBuffer, - ("%s", toCString("Access stub for ", *codeBlock, " ", stubInfo.codeOrigin, " with return point ", successLabel, ": ", listDump(cases)).data())); - - bool doesCalls = false; - for (auto& entry : cases) - doesCalls |= entry->doesCalls(); - - m_stubRoutine = createJITStubRoutine(code, vm, codeBlock, doesCalls, nullptr, codeBlockThatOwnsExceptionHandlers, callSiteIndexForExceptionHandling); - m_watchpoints = WTFMove(state.watchpoints); - if (!state.weakReferences.isEmpty()) - m_weakReferences = std::make_unique>>(WTFMove(state.weakReferences)); - if (verbose) - dataLog("Returning: ", code.code(), "\n"); - return code.code(); -} - -void PolymorphicAccess::aboutToDie() -{ - m_stubRoutine->aboutToDie(); -} - -} // namespace JSC - -namespace WTF { - -using namespace JSC; - -void printInternal(PrintStream& out, AccessCase::AccessType type) -{ - switch (type) { - case AccessCase::Load: - out.print("Load"); - return; - case AccessCase::Transition: - out.print("Transition"); - return; - case AccessCase::Replace: - out.print("Replace"); - return; - case AccessCase::Miss: - out.print("Miss"); - return; - case AccessCase::Getter: - out.print("Getter"); - return; - case AccessCase::Setter: - out.print("Setter"); - return; - case AccessCase::CustomValueGetter: - out.print("CustomValueGetter"); - return; - case AccessCase::CustomAccessorGetter: - out.print("CustomAccessorGetter"); - return; - case AccessCase::CustomValueSetter: - out.print("CustomValueSetter"); - return; - case AccessCase::CustomAccessorSetter: - out.print("CustomAccessorSetter"); - return; - case AccessCase::IntrinsicGetter: - out.print("IntrinsicGetter"); - return; - case AccessCase::InHit: - out.print("InHit"); - return; - case AccessCase::InMiss: - out.print("InMiss"); - return; - case AccessCase::ArrayLength: - out.print("ArrayLength"); - return; - case AccessCase::StringLength: - out.print("StringLength"); - return; - } - - RELEASE_ASSERT_NOT_REACHED(); -} - -} // namespace WTF - -#endif // ENABLE(JIT) - - -- cgit v1.2.1