| /* |
| * Copyright (C) 2008-2018 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. |
| */ |
| |
| #pragma once |
| |
| #include "CodeBlock.h" |
| #include "CodeOrigin.h" |
| #include "Instruction.h" |
| #include "JITStubRoutine.h" |
| #include "MacroAssembler.h" |
| #include "Options.h" |
| #include "RegisterSet.h" |
| #include "Structure.h" |
| #include "StructureSet.h" |
| #include "StructureStubClearingWatchpoint.h" |
| #include "StubInfoSummary.h" |
| |
| namespace JSC { |
| |
| #if ENABLE(JIT) |
| |
| class AccessCase; |
| class AccessGenerationResult; |
| class PolymorphicAccess; |
| |
| enum class AccessType : int8_t { |
| Get, |
| GetWithThis, |
| GetDirect, |
| TryGet, |
| Put, |
| In, |
| InstanceOf |
| }; |
| |
| enum class CacheType : int8_t { |
| Unset, |
| GetByIdSelf, |
| PutByIdReplace, |
| InByIdSelf, |
| Stub, |
| ArrayLength, |
| StringLength |
| }; |
| |
| class StructureStubInfo { |
| WTF_MAKE_NONCOPYABLE(StructureStubInfo); |
| WTF_MAKE_FAST_ALLOCATED; |
| public: |
| StructureStubInfo(AccessType); |
| ~StructureStubInfo(); |
| |
| void initGetByIdSelf(CodeBlock*, Structure* baseObjectStructure, PropertyOffset); |
| void initArrayLength(); |
| void initStringLength(); |
| void initPutByIdReplace(CodeBlock*, Structure* baseObjectStructure, PropertyOffset); |
| void initInByIdSelf(CodeBlock*, Structure* baseObjectStructure, PropertyOffset); |
| |
| AccessGenerationResult addAccessCase(const GCSafeConcurrentJSLocker&, CodeBlock*, const Identifier&, std::unique_ptr<AccessCase>); |
| |
| void reset(CodeBlock*); |
| |
| void deref(); |
| void aboutToDie(); |
| |
| // Check if the stub has weak references that are dead. If it does, then it resets itself, |
| // either entirely or just enough to ensure that those dead pointers don't get used anymore. |
| void visitWeakReferences(CodeBlock*); |
| |
| // This returns true if it has marked everything that it will ever mark. |
| bool propagateTransitions(SlotVisitor&); |
| |
| ALWAYS_INLINE bool considerCaching(CodeBlock* codeBlock, Structure* structure) |
| { |
| // We never cache non-cells. |
| if (!structure) { |
| sawNonCell = true; |
| return false; |
| } |
| |
| // This method is called from the Optimize variants of IC slow paths. The first part of this |
| // method tries to determine if the Optimize variant should really behave like the |
| // non-Optimize variant and leave the IC untouched. |
| // |
| // If we determine that we should do something to the IC then the next order of business is |
| // to determine if this Structure would impact the IC at all. We know that it won't, if we |
| // have already buffered something on its behalf. That's what the bufferedStructures set is |
| // for. |
| |
| everConsidered = true; |
| if (!countdown) { |
| // Check if we have been doing repatching too frequently. If so, then we should cool off |
| // for a while. |
| WTF::incrementWithSaturation(repatchCount); |
| if (repatchCount > Options::repatchCountForCoolDown()) { |
| // We've been repatching too much, so don't do it now. |
| repatchCount = 0; |
| // The amount of time we require for cool-down depends on the number of times we've |
| // had to cool down in the past. The relationship is exponential. The max value we |
| // allow here is 2^256 - 2, since the slow paths may increment the count to indicate |
| // that they'd like to temporarily skip patching just this once. |
| countdown = WTF::leftShiftWithSaturation( |
| static_cast<uint8_t>(Options::initialCoolDownCount()), |
| numberOfCoolDowns, |
| static_cast<uint8_t>(std::numeric_limits<uint8_t>::max() - 1)); |
| WTF::incrementWithSaturation(numberOfCoolDowns); |
| |
| // We may still have had something buffered. Trigger generation now. |
| bufferingCountdown = 0; |
| return true; |
| } |
| |
| // We don't want to return false due to buffering indefinitely. |
| if (!bufferingCountdown) { |
| // Note that when this returns true, it's possible that we will not even get an |
| // AccessCase because this may cause Repatch.cpp to simply do an in-place |
| // repatching. |
| return true; |
| } |
| |
| bufferingCountdown--; |
| |
| // Now protect the IC buffering. We want to proceed only if this is a structure that |
| // we don't already have a case buffered for. Note that if this returns true but the |
| // bufferingCountdown is not zero then we will buffer the access case for later without |
| // immediately generating code for it. |
| // |
| // NOTE: This will behave oddly for InstanceOf if the user varies the prototype but not |
| // the base's structure. That seems unlikely for the canonical use of instanceof, where |
| // the prototype is fixed. |
| bool isNewlyAdded = bufferedStructures.add(structure); |
| if (isNewlyAdded) { |
| VM& vm = *codeBlock->vm(); |
| vm.heap.writeBarrier(codeBlock); |
| } |
| return isNewlyAdded; |
| } |
| countdown--; |
| return false; |
| } |
| |
| StubInfoSummary summary() const; |
| |
| static StubInfoSummary summary(const StructureStubInfo*); |
| |
| bool containsPC(void* pc) const; |
| |
| CodeOrigin codeOrigin; |
| CallSiteIndex callSiteIndex; |
| |
| union { |
| struct { |
| WriteBarrierBase<Structure> baseObjectStructure; |
| PropertyOffset offset; |
| } byIdSelf; |
| PolymorphicAccess* stub; |
| } u; |
| |
| // Represents those structures that already have buffered AccessCases in the PolymorphicAccess. |
| // Note that it's always safe to clear this. If we clear it prematurely, then if we see the same |
| // structure again during this buffering countdown, we will create an AccessCase object for it. |
| // That's not so bad - we'll get rid of the redundant ones once we regenerate. |
| StructureSet bufferedStructures; |
| |
| struct { |
| CodeLocationLabel<JITStubRoutinePtrTag> start; // This is either the start of the inline IC for *byId caches. or the location of patchable jump for 'instanceof' caches. |
| CodeLocationLabel<JSInternalPtrTag> doneLocation; |
| CodeLocationCall<JSInternalPtrTag> slowPathCallLocation; |
| CodeLocationLabel<JITStubRoutinePtrTag> slowPathStartLocation; |
| |
| RegisterSet usedRegisters; |
| |
| uint32_t inlineSize() const |
| { |
| int32_t inlineSize = MacroAssembler::differenceBetweenCodePtr(start, doneLocation); |
| ASSERT(inlineSize >= 0); |
| return inlineSize; |
| } |
| |
| GPRReg baseGPR; |
| GPRReg valueGPR; |
| GPRReg thisGPR; |
| #if USE(JSVALUE32_64) |
| GPRReg valueTagGPR; |
| GPRReg baseTagGPR; |
| GPRReg thisTagGPR; |
| #endif |
| } patch; |
| |
| GPRReg baseGPR() const |
| { |
| return patch.baseGPR; |
| } |
| |
| CodeLocationCall<JSInternalPtrTag> slowPathCallLocation() { return patch.slowPathCallLocation; } |
| CodeLocationLabel<JSInternalPtrTag> doneLocation() { return patch.doneLocation; } |
| CodeLocationLabel<JITStubRoutinePtrTag> slowPathStartLocation() { return patch.slowPathStartLocation; } |
| |
| CodeLocationJump<JSInternalPtrTag> patchableJump() |
| { |
| ASSERT(accessType == AccessType::InstanceOf); |
| return patch.start.jumpAtOffset<JSInternalPtrTag>(0); |
| } |
| |
| JSValueRegs valueRegs() const |
| { |
| return JSValueRegs( |
| #if USE(JSVALUE32_64) |
| patch.valueTagGPR, |
| #endif |
| patch.valueGPR); |
| } |
| |
| |
| AccessType accessType; |
| CacheType cacheType; |
| uint8_t countdown; // We repatch only when this is zero. If not zero, we decrement. |
| uint8_t repatchCount; |
| uint8_t numberOfCoolDowns; |
| uint8_t bufferingCountdown; |
| bool resetByGC : 1; |
| bool tookSlowPath : 1; |
| bool everConsidered : 1; |
| bool prototypeIsKnownObject : 1; // Only relevant for InstanceOf. |
| bool sawNonCell : 1; |
| }; |
| |
| inline CodeOrigin getStructureStubInfoCodeOrigin(StructureStubInfo& structureStubInfo) |
| { |
| return structureStubInfo.codeOrigin; |
| } |
| |
| inline J_JITOperation_ESsiJI appropriateOptimizingGetByIdFunction(AccessType type) |
| { |
| switch (type) { |
| case AccessType::Get: |
| return operationGetByIdOptimize; |
| case AccessType::TryGet: |
| return operationTryGetByIdOptimize; |
| case AccessType::GetDirect: |
| return operationGetByIdDirectOptimize; |
| case AccessType::GetWithThis: |
| default: |
| ASSERT_NOT_REACHED(); |
| return nullptr; |
| } |
| } |
| |
| inline J_JITOperation_EJI appropriateGenericGetByIdFunction(AccessType type) |
| { |
| switch (type) { |
| case AccessType::Get: |
| return operationGetByIdGeneric; |
| case AccessType::TryGet: |
| return operationTryGetByIdGeneric; |
| case AccessType::GetDirect: |
| return operationGetByIdDirectGeneric; |
| case AccessType::GetWithThis: |
| default: |
| ASSERT_NOT_REACHED(); |
| return nullptr; |
| } |
| } |
| |
| #else |
| |
| class StructureStubInfo; |
| |
| #endif // ENABLE(JIT) |
| |
| typedef HashMap<CodeOrigin, StructureStubInfo*, CodeOriginApproximateHash> StubInfoMap; |
| |
| } // namespace JSC |