blob: d3a35acafb0a88735ac020c2d5dae0b003ecea75 [file] [log] [blame]
/*
* 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