ValueProfile does not make it safe to introspect cell values
after garbage collection
https://bugs.webkit.org/show_bug.cgi?id=67354
Reviewed by Gavin Barraclough.
ValueProfile buckets are now weak references, implemented using a
light-weight weak reference mechanism that this patch also adds (the
WeakReferenceHarvester). If a cell stored in a ValueProfile bucket
is not marked, then the bucket is transformed into a Structure
pointer. If the Structure is not marked either, then it is turned
into a ClassInfo pointer.
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::~CodeBlock):
(JSC::CodeBlock::visitAggregate):
(JSC::CodeBlock::visitWeakReferences):
* bytecode/CodeBlock.h:
* bytecode/ValueProfile.h:
(JSC::ValueProfile::ValueProfile):
(JSC::ValueProfile::classInfo):
(JSC::ValueProfile::numberOfInt32s):
(JSC::ValueProfile::numberOfDoubles):
(JSC::ValueProfile::numberOfCells):
(JSC::ValueProfile::numberOfArrays):
(JSC::ValueProfile::probabilityOfArray):
(JSC::ValueProfile::WeakBucket::WeakBucket):
(JSC::ValueProfile::WeakBucket::operator!):
(JSC::ValueProfile::WeakBucket::isEmpty):
(JSC::ValueProfile::WeakBucket::isClassInfo):
(JSC::ValueProfile::WeakBucket::isStructure):
(JSC::ValueProfile::WeakBucket::asStructure):
(JSC::ValueProfile::WeakBucket::asClassInfo):
(JSC::ValueProfile::WeakBucket::getClassInfo):
* heap/Heap.cpp:
(JSC::Heap::harvestWeakReferences):
(JSC::Heap::markRoots):
* heap/Heap.h:
* heap/MarkStack.cpp:
(JSC::SlotVisitor::drain):
(JSC::SlotVisitor::harvestWeakReferences):
* heap/MarkStack.h:
(JSC::MarkStack::addWeakReferenceHarvester):
(JSC::MarkStack::MarkStack):
(JSC::MarkStack::appendUnbarrieredPointer):
* heap/SlotVisitor.h:
* heap/WeakReferenceHarvester.h: Added.
(JSC::WeakReferenceHarvester::WeakReferenceHarvester):
(JSC::WeakReferenceHarvester::~WeakReferenceHarvester):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@94477 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index f35d736..fb1f92b 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -1433,19 +1433,21 @@
CodeBlock::~CodeBlock()
{
#if ENABLE(VERBOSE_VALUE_PROFILE)
- printf("ValueProfile for %p:\n", this);
+ fprintf(stderr, "ValueProfile for %p:\n", this);
for (unsigned i = 0; i < numberOfValueProfiles(); ++i) {
ValueProfile* profile = valueProfile(i);
if (profile->bytecodeOffset < 0) {
ASSERT(profile->bytecodeOffset == -1);
- printf(" arg = %u: ", i + 1);
+ fprintf(stderr, " arg = %u: ", i + 1);
} else
- printf(" bc = %d: ", profile->bytecodeOffset);
- printf("samples = %u, int32 = %u, double = %u, cell = %u\n",
- profile->numberOfSamples(),
- profile->probabilityOfInt32(),
- profile->probabilityOfDouble(),
- profile->probabilityOfCell());
+ fprintf(stderr, " bc = %d: ", profile->bytecodeOffset);
+ fprintf(stderr,
+ "samples = %u, int32 = %u, double = %u, cell = %u, array = %u\n",
+ profile->numberOfSamples(),
+ profile->probabilityOfInt32(),
+ profile->probabilityOfDouble(),
+ profile->probabilityOfCell(),
+ profile->probabilityOfArray());
}
#endif
@@ -1515,6 +1517,8 @@
void CodeBlock::visitAggregate(SlotVisitor& visitor)
{
+ bool handleWeakReferences = false;
+
visitor.append(&m_globalObject);
visitor.append(&m_ownerExecutable);
if (m_rareData) {
@@ -1562,6 +1566,64 @@
}
}
#endif
+
+#if ENABLE(VALUE_PROFILER)
+ for (unsigned profileIndex = 0; profileIndex < numberOfValueProfiles(); ++profileIndex) {
+ ValueProfile* profile = valueProfile(profileIndex);
+
+ for (unsigned index = 0; index < ValueProfile::numberOfBuckets; ++index) {
+ if (!profile->buckets[index]) {
+ if (!!profile->weakBuckets[index])
+ handleWeakReferences = true;
+ continue;
+ }
+
+ if (!JSValue::decode(profile->buckets[index]).isCell()) {
+ profile->weakBuckets[index] = ValueProfile::WeakBucket();
+ continue;
+ }
+
+ handleWeakReferences = true;
+ }
+ }
+#endif
+
+ if (handleWeakReferences)
+ visitor.addWeakReferenceHarvester(this);
+}
+
+void CodeBlock::visitWeakReferences(SlotVisitor&)
+{
+#if ENABLE(VALUE_PROFILER)
+ for (unsigned profileIndex = 0; profileIndex < numberOfValueProfiles(); ++profileIndex) {
+ ValueProfile* profile = valueProfile(profileIndex);
+
+ for (unsigned index = 0; index < ValueProfile::numberOfBuckets; ++index) {
+ if (!!profile->buckets[index]) {
+ JSValue value = JSValue::decode(profile->buckets[index]);
+ if (!value.isCell())
+ continue;
+
+ JSCell* cell = value.asCell();
+ if (Heap::isMarked(cell))
+ continue;
+
+ profile->buckets[index] = JSValue::encode(JSValue());
+ profile->weakBuckets[index] = cell->structure();
+ }
+
+ ValueProfile::WeakBucket weak = profile->weakBuckets[index];
+ if (!weak || weak.isClassInfo())
+ continue;
+
+ ASSERT(weak.isStructure());
+ if (Heap::isMarked(weak.asStructure()))
+ continue;
+
+ profile->weakBuckets[index] = weak.asStructure()->classInfo();
+ }
+ }
+#endif
}
HandlerInfo* CodeBlock::handlerForBytecodeOffset(unsigned bytecodeOffset)
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.h b/Source/JavaScriptCore/bytecode/CodeBlock.h
index 08cad63..5a61031 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.h
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.h
@@ -39,6 +39,7 @@
#include "Nodes.h"
#include "RegExpObject.h"
#include "UString.h"
+#include "WeakReferenceHarvester.h"
#include "ValueProfile.h"
#include <wtf/FastAllocBase.h>
#include <wtf/PassOwnPtr.h>
@@ -205,7 +206,7 @@
}
#endif
- class CodeBlock {
+ class CodeBlock: public WeakReferenceHarvester {
WTF_MAKE_FAST_ALLOCATED;
friend class JIT;
protected:
@@ -218,6 +219,7 @@
virtual ~CodeBlock();
void visitAggregate(SlotVisitor&);
+ void visitWeakReferences(SlotVisitor&);
static void dumpStatistics();
diff --git a/Source/JavaScriptCore/bytecode/ValueProfile.h b/Source/JavaScriptCore/bytecode/ValueProfile.h
index 3890f7a..a703f98 100644
--- a/Source/JavaScriptCore/bytecode/ValueProfile.h
+++ b/Source/JavaScriptCore/bytecode/ValueProfile.h
@@ -29,6 +29,8 @@
#ifndef ValueProfile_h
#define ValueProfile_h
+#include "JSArray.h"
+#include "Structure.h"
#include "WriteBarrier.h"
namespace JSC {
@@ -45,14 +47,25 @@
: bytecodeOffset(bytecodeOffset)
{
for (unsigned i = 0; i < numberOfBuckets; ++i)
- buckets[i].setWithoutWriteBarrier(JSValue());
+ buckets[i] = JSValue::encode(JSValue());
+ }
+
+ const ClassInfo* classInfo(unsigned bucket) const
+ {
+ if (!!buckets[bucket]) {
+ JSValue value = JSValue::decode(buckets[bucket]);
+ if (!value.isCell())
+ return 0;
+ return value.asCell()->structure()->classInfo();
+ }
+ return weakBuckets[bucket].getClassInfo();
}
unsigned numberOfSamples() const
{
unsigned result = 0;
for (unsigned i = 0; i < numberOfBuckets; ++i) {
- if (!!buckets[i])
+ if (!!buckets[i] || !!weakBuckets[i])
result++;
}
return result;
@@ -69,7 +82,7 @@
{
unsigned result = 0;
for (unsigned i = 0; i < numberOfBuckets; ++i) {
- if (!!buckets[i] && buckets[i].get().isInt32())
+ if (!!buckets[i] && JSValue::decode(buckets[i]).isInt32())
result++;
}
return result;
@@ -79,7 +92,7 @@
{
unsigned result = 0;
for (unsigned i = 0; i < numberOfBuckets; ++i) {
- if (!!buckets[i] && buckets[i].get().isDouble())
+ if (!!buckets[i] && JSValue::decode(buckets[i]).isDouble())
result++;
}
return result;
@@ -89,7 +102,17 @@
{
unsigned result = 0;
for (unsigned i = 0; i < numberOfBuckets; ++i) {
- if (!!buckets[i] && buckets[i].get().isCell())
+ if (!!classInfo(i))
+ result++;
+ }
+ return result;
+ }
+
+ unsigned numberOfArrays() const
+ {
+ unsigned result = 0;
+ for (unsigned i = 0; i < numberOfBuckets; ++i) {
+ if (classInfo(i) == &JSArray::s_info)
result++;
}
return result;
@@ -115,8 +138,77 @@
return computeProbability(numberOfCells(), numberOfSamples());
}
+ unsigned probabilityOfArray() const
+ {
+ return computeProbability(numberOfArrays(), numberOfSamples());
+ }
+
int bytecodeOffset; // -1 for prologue
- WriteBarrierBase<Unknown> buckets[numberOfBuckets];
+ EncodedJSValue buckets[numberOfBuckets];
+
+ class WeakBucket {
+ public:
+ WeakBucket()
+ : m_value(0)
+ {
+ }
+
+ WeakBucket(Structure* structure)
+ : m_value(reinterpret_cast<uintptr_t>(structure))
+ {
+ }
+
+ WeakBucket(const ClassInfo* classInfo)
+ : m_value(reinterpret_cast<uintptr_t>(classInfo) | 1)
+ {
+ }
+
+ bool operator!() const
+ {
+ return !m_value;
+ }
+
+ bool isEmpty() const
+ {
+ return !m_value;
+ }
+
+ bool isClassInfo() const
+ {
+ return !!(m_value & 1);
+ }
+
+ bool isStructure() const
+ {
+ return !isEmpty() && !isClassInfo();
+ }
+
+ Structure* asStructure() const
+ {
+ ASSERT(isStructure());
+ return reinterpret_cast<Structure*>(m_value);
+ }
+
+ const ClassInfo* asClassInfo() const
+ {
+ ASSERT(isClassInfo());
+ return reinterpret_cast<ClassInfo*>(m_value & ~static_cast<uintptr_t>(1));
+ }
+
+ const ClassInfo* getClassInfo() const
+ {
+ if (isEmpty())
+ return 0;
+ if (isClassInfo())
+ return asClassInfo();
+ return asStructure()->classInfo();
+ }
+
+ private:
+ uintptr_t m_value;
+ };
+
+ WeakBucket weakBuckets[numberOfBuckets]; // this is not covered by a write barrier because it is only set from GC
};
inline int getValueProfileBytecodeOffset(ValueProfile* valueProfile)