https://bugs.webkit.org/show_bug.cgi?id=124465
Reviewed by Sam Weinig.
We previously represented the state of watchpoint sets using two booleans. But that
makes it awkward to case over the state.
We also previously supported a watchpoint set being both watched and invalidated. We
never used that capability, and its presence was just purely confusing.
This turns the whole thing into an enum.
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::branch8):
* assembler/MacroAssemblerARMv7.h:
(JSC::MacroAssemblerARMv7::branch8):
* assembler/MacroAssemblerX86.h:
(JSC::MacroAssemblerX86::branch8):
* assembler/MacroAssemblerX86_64.h:
(JSC::MacroAssemblerX86_64::branch8):
* bytecode/Watchpoint.cpp:
(JSC::WatchpointSet::WatchpointSet):
(JSC::WatchpointSet::add):
(JSC::WatchpointSet::notifyWriteSlow):
(JSC::InlineWatchpointSet::inflateSlow):
* bytecode/Watchpoint.h:
(JSC::WatchpointSet::state):
(JSC::WatchpointSet::isStillValid):
(JSC::WatchpointSet::startWatching):
(JSC::WatchpointSet::notifyWrite):
(JSC::WatchpointSet::addressOfState):
(JSC::InlineWatchpointSet::InlineWatchpointSet):
(JSC::InlineWatchpointSet::hasBeenInvalidated):
(JSC::InlineWatchpointSet::startWatching):
(JSC::InlineWatchpointSet::notifyWrite):
(JSC::InlineWatchpointSet::decodeState):
(JSC::InlineWatchpointSet::encodeState):
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emitVarInjectionCheck):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emitVarInjectionCheck):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/JSFunction.cpp:
(JSC::JSFunction::JSFunction):
* runtime/JSFunctionInlines.h:
(JSC::JSFunction::JSFunction):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::JSGlobalObject):
* runtime/Structure.cpp:
(JSC::Structure::Structure):
* runtime/SymbolTable.cpp:
(JSC::SymbolTableEntry::attemptToWatch):
* runtime/SymbolTable.h:
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@159395
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
2013-11-16 Filip Pizlo <fpizlo@apple.com>
+ Simplify WatchpointSet state tracking
+ https://bugs.webkit.org/show_bug.cgi?id=124465
+
+ Reviewed by Sam Weinig.
+
+ We previously represented the state of watchpoint sets using two booleans. But that
+ makes it awkward to case over the state.
+
+ We also previously supported a watchpoint set being both watched and invalidated. We
+ never used that capability, and its presence was just purely confusing.
+
+ This turns the whole thing into an enum.
+
+ * assembler/MacroAssemblerARM64.h:
+ (JSC::MacroAssemblerARM64::branch8):
+ * assembler/MacroAssemblerARMv7.h:
+ (JSC::MacroAssemblerARMv7::branch8):
+ * assembler/MacroAssemblerX86.h:
+ (JSC::MacroAssemblerX86::branch8):
+ * assembler/MacroAssemblerX86_64.h:
+ (JSC::MacroAssemblerX86_64::branch8):
+ * bytecode/Watchpoint.cpp:
+ (JSC::WatchpointSet::WatchpointSet):
+ (JSC::WatchpointSet::add):
+ (JSC::WatchpointSet::notifyWriteSlow):
+ (JSC::InlineWatchpointSet::inflateSlow):
+ * bytecode/Watchpoint.h:
+ (JSC::WatchpointSet::state):
+ (JSC::WatchpointSet::isStillValid):
+ (JSC::WatchpointSet::startWatching):
+ (JSC::WatchpointSet::notifyWrite):
+ (JSC::WatchpointSet::addressOfState):
+ (JSC::InlineWatchpointSet::InlineWatchpointSet):
+ (JSC::InlineWatchpointSet::hasBeenInvalidated):
+ (JSC::InlineWatchpointSet::startWatching):
+ (JSC::InlineWatchpointSet::notifyWrite):
+ (JSC::InlineWatchpointSet::decodeState):
+ (JSC::InlineWatchpointSet::encodeState):
+ * jit/JITPropertyAccess.cpp:
+ (JSC::JIT::emitVarInjectionCheck):
+ * jit/JITPropertyAccess32_64.cpp:
+ (JSC::JIT::emitVarInjectionCheck):
+ * llint/LowLevelInterpreter.asm:
+ * llint/LowLevelInterpreter32_64.asm:
+ * llint/LowLevelInterpreter64.asm:
+ * runtime/JSFunction.cpp:
+ (JSC::JSFunction::JSFunction):
+ * runtime/JSFunctionInlines.h:
+ (JSC::JSFunction::JSFunction):
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::JSGlobalObject):
+ * runtime/Structure.cpp:
+ (JSC::Structure::Structure):
+ * runtime/SymbolTable.cpp:
+ (JSC::SymbolTableEntry::attemptToWatch):
+ * runtime/SymbolTable.h:
+
+2013-11-16 Filip Pizlo <fpizlo@apple.com>
+
FTL should have an explicit notion of bytecode liveness
https://bugs.webkit.org/show_bug.cgi?id=124181
return branch32(cond, memoryTempRegister, right);
}
+ Jump branch8(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right)
+ {
+ ASSERT(!(0xffffff00 & right.m_value));
+ load8(left, getCachedMemoryTempRegisterIDAndInvalidate());
+ return branch32(cond, memoryTempRegister, right);
+ }
+
Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask)
{
m_assembler.tst<32>(reg, mask);
return branch32(cond, addressTempRegister, right);
}
+ Jump branch8(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right)
+ {
+ load8(left, addressTempRegister);
+ return branch32(cond, addressTempRegister, right);
+ }
+
Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask)
{
m_assembler.tst(reg, mask);
using MacroAssemblerX86Common::loadDouble;
using MacroAssemblerX86Common::storeDouble;
using MacroAssemblerX86Common::convertInt32ToDouble;
+ using MacroAssemblerX86Common::branch8;
using MacroAssemblerX86Common::branchTest8;
void add32(TrustedImm32 imm, RegisterID src, RegisterID dest)
return DataLabelPtr(this);
}
+ Jump branch8(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right)
+ {
+ m_assembler.cmpb_im(right.m_value, left.m_ptr);
+ return Jump(m_assembler.jCC(x86Condition(cond)));
+ }
+
Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1))
{
ASSERT(mask.m_value >= -128 && mask.m_value <= 255);
return label;
}
+ using MacroAssemblerX86Common::branch8;
+ Jump branch8(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right)
+ {
+ MacroAssemblerX86Common::move(TrustedImmPtr(left.m_ptr), scratchRegister);
+ return MacroAssemblerX86Common::branch8(cond, Address(scratchRegister), right);
+ }
+
using MacroAssemblerX86Common::branchTest8;
Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1))
{
remove();
}
-WatchpointSet::WatchpointSet(InitialWatchpointSetMode mode)
- : m_isWatched(mode == InitializedWatching)
- , m_isInvalidated(false)
+WatchpointSet::WatchpointSet(WatchpointState state)
+ : m_state(state)
{
}
void WatchpointSet::add(Watchpoint* watchpoint)
{
ASSERT(!isCompilationThread());
+ ASSERT(state() != IsInvalidated);
if (!watchpoint)
return;
m_set.push(watchpoint);
- m_isWatched = true;
+ m_state = IsWatched;
}
void WatchpointSet::notifyWriteSlow()
{
- ASSERT(m_isWatched);
+ ASSERT(state() == IsWatched);
fireAllWatchpoints();
- m_isWatched = false;
- m_isInvalidated = true;
+ m_state = IsInvalidated;
WTF::storeStoreFence();
}
{
ASSERT(isThin());
ASSERT(!isCompilationThread());
- WatchpointSet* fat = adoptRef(new WatchpointSet(InitializedBlind)).leakRef();
- if (m_data & IsInvalidatedFlag)
- fat->m_isInvalidated = true;
- if (m_data & IsWatchedFlag)
- fat->m_isWatched = true;
+ WatchpointSet* fat = adoptRef(new WatchpointSet(decodeState(m_data))).leakRef();
WTF::storeStoreFence();
m_data = bitwise_cast<uintptr_t>(fat);
return fat;
virtual void fireInternal() = 0;
};
-enum InitialWatchpointSetMode { InitializedWatching, InitializedBlind };
+enum WatchpointState {
+ ClearWatchpoint,
+ IsWatched,
+ IsInvalidated
+};
class InlineWatchpointSet;
class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> {
friend class LLIntOffsetsExtractor;
public:
- WatchpointSet(InitialWatchpointSetMode);
+ WatchpointSet(WatchpointState);
~WatchpointSet(); // Note that this will not fire any of the watchpoints; if you need to know when a WatchpointSet dies then you need a separate mechanism for this.
+ WatchpointState state() const { return static_cast<WatchpointState>(m_state); }
+
// It is safe to call this from another thread. It may return true
// even if the set actually had been invalidated, but that ought to happen
// only in the case of races, and should be rare. Guarantees that if you
bool isStillValid() const
{
WTF::loadLoadFence();
- return !m_isInvalidated;
+ return state() != IsInvalidated;
}
// Like isStillValid(), may be called from another thread.
bool hasBeenInvalidated() const { return !isStillValid(); }
// watchpoint would have fired. That's a pretty good indication that you
// probably don't want to set watchpoints, since we typically don't want to
// set watchpoints that we believe will actually be fired.
- void startWatching() { m_isWatched = true; }
+ void startWatching()
+ {
+ ASSERT(state() != IsInvalidated);
+ m_state = IsWatched;
+ }
void notifyWrite()
{
- if (!m_isWatched)
+ if (state() != IsWatched)
return;
notifyWriteSlow();
}
-
- bool* addressOfIsWatched() { return &m_isWatched; }
- bool* addressOfIsInvalidated() { return &m_isInvalidated; }
+
+ int8_t* addressOfState() { return &m_state; }
JS_EXPORT_PRIVATE void notifyWriteSlow(); // Call only if you've checked isWatched.
friend class InlineWatchpointSet;
SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set;
- bool m_isWatched;
- bool m_isInvalidated;
+ int8_t m_state;
};
// InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which
class InlineWatchpointSet {
WTF_MAKE_NONCOPYABLE(InlineWatchpointSet);
public:
- InlineWatchpointSet(InitialWatchpointSetMode mode)
- : m_data((mode == InitializedWatching ? IsWatchedFlag : 0) | IsThinFlag)
+ InlineWatchpointSet(WatchpointState state)
+ : m_data(encodeState(state))
{
}
WTF::loadLoadFence();
return fat(data)->hasBeenInvalidated();
}
- return data & IsInvalidatedFlag;
+ return decodeState(data) == IsInvalidated;
}
// Like hasBeenInvalidated(), may be called from another thread.
fat()->startWatching();
return;
}
- m_data |= IsWatchedFlag;
+ ASSERT(decodeState(m_data) != IsInvalidated);
+ m_data = encodeState(IsWatched);
}
void notifyWrite()
fat()->notifyWrite();
return;
}
- if (!(m_data & IsWatchedFlag))
+ if (decodeState(m_data) == ClearWatchpoint)
return;
- m_data |= IsInvalidatedFlag;
+ m_data = encodeState(IsInvalidated);
WTF::storeStoreFence();
}
private:
static const uintptr_t IsThinFlag = 1;
- static const uintptr_t IsInvalidatedFlag = 2;
- static const uintptr_t IsWatchedFlag = 4;
+ static const uintptr_t StateMask = 6;
+ static const uintptr_t StateShift = 1;
static bool isThin(uintptr_t data) { return data & IsThinFlag; }
static bool isFat(uintptr_t data) { return !isThin(data); }
+ static WatchpointState decodeState(uintptr_t data)
+ {
+ ASSERT(isThin(data));
+ return static_cast<WatchpointState>((data & StateMask) >> StateShift);
+ }
+
+ static uintptr_t encodeState(WatchpointState state)
+ {
+ return (state << StateShift) | IsThinFlag;
+ }
+
bool isThin() const { return isThin(m_data); }
bool isFat() const { return isFat(m_data); };
{
if (!needsVarInjectionChecks)
return;
- addSlowCase(branchTest8(NonZero, AbsoluteAddress(m_codeBlock->globalObject()->varInjectionWatchpoint()->addressOfIsInvalidated())));
+ addSlowCase(branch8(Equal, AbsoluteAddress(m_codeBlock->globalObject()->varInjectionWatchpoint()->addressOfState()), TrustedImm32(IsInvalidated)));
}
void JIT::emitResolveClosure(int dst, bool needsVarInjectionChecks, unsigned depth)
{
if (!needsVarInjectionChecks)
return;
- addSlowCase(branchTest8(NonZero, AbsoluteAddress(m_codeBlock->globalObject()->varInjectionWatchpoint()->addressOfIsInvalidated())));
+ addSlowCase(branch8(Equal, AbsoluteAddress(m_codeBlock->globalObject()->varInjectionWatchpoint()->addressOfState()), TrustedImm32(IsInvalidated)));
}
void JIT::emitResolveClosure(int dst, bool needsVarInjectionChecks, unsigned depth)
const LowestTag = DeletedValueTag
end
+# Watchpoint states
+const ClearWatchpoint = 0
+const IsWatched = 1
+const IsInvalidated = 2
+
# Some register conventions.
if JSVALUE64
# - Use a pair of registers to represent the PC: one register for the
loadp CodeBlock[cfr], t0
loadp CodeBlock::m_globalObject[t0], t0
loadp JSGlobalObject::m_varInjectionWatchpoint[t0], t0
- btbnz WatchpointSet::m_isInvalidated[t0], slowPath
+ bbeq WatchpointSet::m_state[t0], IsInvalidated, slowPath
end
macro resolveScope()
loadp CodeBlock[cfr], t0
loadp CodeBlock::m_globalObject[t0], t0
loadp JSGlobalObject::m_varInjectionWatchpoint[t0], t0
- btbnz WatchpointSet::m_isInvalidated[t0], slowPath
+ bbeq WatchpointSet::m_state[t0], IsInvalidated, slowPath
end
macro resolveScope()
// was clobbered exactly once, but that seems like overkill. In almost all cases it will be
// clobbered once, and if it's clobbered more than once, that will probably only occur
// before we started optimizing, anyway.
- , m_allocationProfileWatchpoint(InitializedBlind)
+ , m_allocationProfileWatchpoint(ClearWatchpoint)
{
}
: Base(vm, scope->globalObject()->functionStructure())
, m_executable(vm, this, executable)
, m_scope(vm, this, scope)
- , m_allocationProfileWatchpoint(InitializedBlind) // See comment in JSFunction.cpp concerning the reason for using InitializedBlind as opposed to InitializedWatching.
+ , m_allocationProfileWatchpoint(ClearWatchpoint) // See comment in JSFunction.cpp concerning the reason for using ClearWatchpoint as opposed to IsWatched.
{
}
JSGlobalObject::JSGlobalObject(VM& vm, Structure* structure, const GlobalObjectMethodTable* globalObjectMethodTable)
: Base(vm, structure, 0)
- , m_masqueradesAsUndefinedWatchpoint(adoptRef(new WatchpointSet(InitializedWatching)))
- , m_havingABadTimeWatchpoint(adoptRef(new WatchpointSet(InitializedWatching)))
- , m_varInjectionWatchpoint(adoptRef(new WatchpointSet(InitializedWatching)))
+ , m_masqueradesAsUndefinedWatchpoint(adoptRef(new WatchpointSet(IsWatched)))
+ , m_havingABadTimeWatchpoint(adoptRef(new WatchpointSet(IsWatched)))
+ , m_varInjectionWatchpoint(adoptRef(new WatchpointSet(IsWatched)))
, m_weakRandom(Options::forceWeakRandomSeed() ? Options::forcedWeakRandomSeed() : static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)))
, m_evalEnabled(true)
, m_globalObjectMethodTable(globalObjectMethodTable ? globalObjectMethodTable : &s_globalObjectMethodTable)
, m_globalObject(vm, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull)
, m_prototype(vm, this, prototype)
, m_classInfo(classInfo)
- , m_transitionWatchpointSet(InitializedWatching)
+ , m_transitionWatchpointSet(IsWatched)
, m_offset(invalidOffset)
, m_typeInfo(typeInfo)
, m_indexingType(indexingType)
: JSCell(CreatingEarlyCell)
, m_prototype(vm, this, jsNull())
, m_classInfo(info())
- , m_transitionWatchpointSet(InitializedWatching)
+ , m_transitionWatchpointSet(IsWatched)
, m_offset(invalidOffset)
, m_typeInfo(CompoundType, OverridesVisitChildren)
, m_indexingType(0)
: JSCell(vm, vm.structureStructure.get())
, m_prototype(vm, this, previous->storedPrototype())
, m_classInfo(previous->m_classInfo)
- , m_transitionWatchpointSet(InitializedWatching)
+ , m_transitionWatchpointSet(IsWatched)
, m_offset(invalidOffset)
, m_typeInfo(previous->typeInfo().type(), previous->typeInfo().flags() & ~StructureHasRareData)
, m_indexingType(previous->indexingTypeIncludingHistory())
{
FatEntry* entry = inflate();
if (!entry->m_watchpoints)
- entry->m_watchpoints = adoptRef(new WatchpointSet(InitializedWatching));
-}
-
-bool* SymbolTableEntry::addressOfIsWatched()
-{
- ASSERT(couldBeWatched());
- return fatEntry()->m_watchpoints->addressOfIsWatched();
+ entry->m_watchpoints = adoptRef(new WatchpointSet(IsWatched));
}
void SymbolTableEntry::addWatchpoint(Watchpoint* watchpoint)
// immediately after a call to attemptToWatch().
void attemptToWatch();
- bool* addressOfIsWatched();
-
void addWatchpoint(Watchpoint*);
WatchpointSet* watchpointSet()