Skip to content

Commit

Permalink
Cherry-pick 1ed1e4a. rdar://problem/98219110
Browse files Browse the repository at this point in the history
    [JSC] Make JSMap and JSSet construction more simple and efficient
    https://bugs.webkit.org/show_bug.cgi?id=243557
    rdar://98068082

    Reviewed by Mark Lam and Saam Barati.

    This patch makes the initial buffer of JSMap / JSSet nullptr so that we can make allocation of them
    simpler and efficient for non-using case. It cleans up many code in module loader etc. And it paves
    the way to allocating them from DFG and FTL efficiently. It also cleans up SerializedScriptValue
    implementation.

    * JSTests/stress/map-clear-get.js: Added.
    (shouldBe):
    (test):
    * JSTests/stress/set-clear-has.js: Added.
    (shouldBe):
    (set clear):
    (set shouldBe):
    (set new):
    * Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp:
    (JSC::DFG::SpeculativeJIT::compile):
    * Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp:
    (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
    * Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp:
    (JSC::AbstractModuleRecord::finishCreation):
    * Source/JavaScriptCore/runtime/HashMapImpl.h:
    (JSC::HashMapBuffer::tryCreate):
    (JSC::HashMapImpl::HashMapImpl):
    (JSC::HashMapBuffer::create): Deleted.
    (JSC::HashMapImpl::shouldRehashAfterAdd const): Deleted.
    * Source/JavaScriptCore/runtime/HashMapImplInlines.h:
    (JSC::shouldShrink):
    (JSC::shouldRehash):
    (JSC::nextCapacity):
    (JSC::HashMapImpl<HashMapBucketType>::finishCreation):
    (JSC::HashMapImpl<HashMapBucketType>::add):
    (JSC::HashMapImpl<HashMapBucketType>::addNormalized):
    (JSC::HashMapImpl<HashMapBucketType>::remove):
    (JSC::HashMapImpl<HashMapBucketType>::clear):
    (JSC::HashMapImpl<HashMapBucketType>::setUpHeadAndTail):
    (JSC::HashMapImpl<HashMapBucketType>::addNormalizedNonExistingForCloning):
    (JSC::HashMapImpl<HashMapBucketType>::addNormalizedNonExistingForCloningInternal):
    (JSC::HashMapImpl<HashMapBucketType>::addNormalizedInternal):
    (JSC::HashMapImpl<HashMapBucketType>::findBucketAlreadyHashedAndNormalized):
    (JSC::HashMapImpl<HashMapBucketType>::rehash):
    (JSC::HashMapImpl<HashMapBucketType>::makeAndSetNewBuffer):
    (JSC::HashMapImpl<HashMapBucketType>::assertBufferIsEmpty):
    (JSC::shouldRehashAfterAdd): Deleted.
    (JSC::HashMapImpl<HashMapBucketType>::assertBufferIsEmpty const): Deleted.
    * Source/JavaScriptCore/runtime/JSMap.h:
    * Source/JavaScriptCore/runtime/JSModuleLoader.cpp:
    (JSC::JSModuleLoader::finishCreation):
    * Source/JavaScriptCore/runtime/JSSet.h:
    * Source/JavaScriptCore/runtime/MapConstructor.cpp:
    (JSC::JSC_DEFINE_HOST_FUNCTION):
    * Source/JavaScriptCore/runtime/MapPrototype.cpp:
    (JSC::JSC_DEFINE_HOST_FUNCTION):
    * Source/JavaScriptCore/runtime/SetConstructor.cpp:
    (JSC::JSC_DEFINE_HOST_FUNCTION):
    * Source/JavaScriptCore/runtime/SetPrototype.cpp:
    (JSC::JSC_DEFINE_HOST_FUNCTION):
    * Source/JavaScriptCore/runtime/WeakMapImplInlines.h:
    (JSC::WeakMapImpl<WeakMapBucket>::shouldRehashAfterAdd const):
    * Source/WebCore/bindings/js/JSDOMMapLike.cpp:
    (WebCore::getBackingMap):
    * Source/WebCore/bindings/js/JSDOMSetLike.cpp:
    (WebCore::getBackingSet):
    * Source/WebCore/bindings/js/SerializedScriptValue.cpp:
    (WebCore::CloneDeserializer::deserialize):

    Canonical link: https://commits.webkit.org/253133@main

Canonical link: https://commits.webkit.org/252432.95@safari-7614.1.25.2-branch
  • Loading branch information
Constellation authored and alancoon committed Aug 8, 2022
1 parent a1864ad commit e4b5df4
Show file tree
Hide file tree
Showing 18 changed files with 170 additions and 146 deletions.
17 changes: 17 additions & 0 deletions JSTests/stress/map-clear-get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error('bad value: ' + actual);
}

function test(map, key)
{
return map.get(key);
}
noInline(test);

var map = new Map();
for (var i = 0; i < 1e4; ++i) {
map.clear();
shouldBe(test(map, {}), undefined);
shouldBe(test(map, {t:42}), undefined);
}
17 changes: 17 additions & 0 deletions JSTests/stress/set-clear-has.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error('bad value: ' + actual);
}

function test(set, key)
{
return set.has(key);
}
noInline(test);

var set = new Set();
for (var i = 0; i < 1e4; ++i) {
set.clear();
shouldBe(test(set, {}), false);
shouldBe(test(set, {t:42}), false);
}
10 changes: 7 additions & 3 deletions Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5000,8 +5000,11 @@ void SpeculativeJIT::compile(Node* node)
if (node->child2().useKind() != UntypedUse)
speculate(node, node->child2());

m_jit.load32(MacroAssembler::Address(mapGPR, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfCapacity()), maskGPR);
CCallHelpers::JumpList notPresentInTable;

m_jit.loadPtr(MacroAssembler::Address(mapGPR, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfBuffer()), bufferGPR);
notPresentInTable.append(m_jit.branchTestPtr(CCallHelpers::Zero, bufferGPR));
m_jit.load32(MacroAssembler::Address(mapGPR, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfCapacity()), maskGPR);
m_jit.sub32(TrustedImm32(1), maskGPR);
m_jit.move(hashGPR, indexGPR);

Expand All @@ -5013,8 +5016,9 @@ void SpeculativeJIT::compile(Node* node)
m_jit.and32(maskGPR, indexGPR);
m_jit.loadPtr(MacroAssembler::BaseIndex(bufferGPR, indexGPR, MacroAssembler::TimesEight), bucketGPR);
m_jit.move(bucketGPR, resultGPR);
auto notPresentInTable = m_jit.branchPtr(MacroAssembler::Equal,
bucketGPR, TrustedImmPtr(bitwise_cast<size_t>(HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::emptyValue())));

notPresentInTable.append(m_jit.branchPtr(MacroAssembler::Equal,
bucketGPR, TrustedImmPtr(bitwise_cast<size_t>(HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::emptyValue()))));
loopAround.append(m_jit.branchPtr(MacroAssembler::Equal,
bucketGPR, TrustedImmPtr(bitwise_cast<size_t>(HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::deletedValue()))));

Expand Down
8 changes: 5 additions & 3 deletions Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12340,6 +12340,7 @@ IGNORE_CLANG_WARNINGS_END
void compileGetMapBucket()
{
JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic);
LBasicBlock indexSetUp = m_out.newBlock();
LBasicBlock loopStart = m_out.newBlock();
LBasicBlock loopAround = m_out.newBlock();
LBasicBlock slowPath = m_out.newBlock();
Expand All @@ -12348,8 +12349,6 @@ IGNORE_CLANG_WARNINGS_END
LBasicBlock notDeletedValue = m_out.newBlock();
LBasicBlock continuation = m_out.newBlock();

LBasicBlock lastNext = m_out.insertNewBlocksBefore(loopStart);

LValue map;
if (m_node->child1().useKind() == MapObjectUse)
map = lowMapObject(m_node->child1());
Expand All @@ -12365,8 +12364,11 @@ IGNORE_CLANG_WARNINGS_END
LValue hash = lowInt32(m_node->child3());

LValue buffer = m_out.loadPtr(map, m_heaps.HashMapImpl_buffer);
LValue mask = m_out.sub(m_out.load32(map, m_heaps.HashMapImpl_capacity), m_out.int32One);

m_out.branch(m_out.isNull(buffer), unsure(notPresentInTable), unsure(indexSetUp));

LBasicBlock lastNext = m_out.appendTo(indexSetUp, loopStart);
LValue mask = m_out.sub(m_out.load32(map, m_heaps.HashMapImpl_capacity), m_out.int32One);
ValueFromBlock indexStart = m_out.anchor(hash);
m_out.jump(loopStart);

Expand Down
6 changes: 1 addition & 5 deletions Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ AbstractModuleRecord::AbstractModuleRecord(VM& vm, Structure* structure, const I

void AbstractModuleRecord::finishCreation(JSGlobalObject* globalObject, VM& vm)
{
DeferTerminationForAWhile deferScope(vm);
auto scope = DECLARE_CATCH_SCOPE(vm);

Base::finishCreation(vm);
ASSERT(inherits(info()));

Expand All @@ -62,8 +59,7 @@ void AbstractModuleRecord::finishCreation(JSGlobalObject* globalObject, VM& vm)
for (unsigned index = 0; index < values.size(); ++index)
Base::internalField(index).set(vm, this, values[index]);

JSMap* map = JSMap::create(globalObject, vm, globalObject->mapStructure());
scope.releaseAssertNoException();
JSMap* map = JSMap::create(vm, globalObject->mapStructure());
m_dependenciesMap.set(vm, this, map);
putDirect(vm, Identifier::fromString(vm, "dependenciesMap"_s), m_dependenciesMap.get());
}
Expand Down
44 changes: 14 additions & 30 deletions Source/JavaScriptCore/runtime/HashMapImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ class HashMapBuffer {
return bitwise_cast<BucketType**>(this);
}

static HashMapBuffer* create(JSGlobalObject* globalObject, VM& vm, JSCell*, uint32_t capacity)
static HashMapBuffer* tryCreate(JSGlobalObject* globalObject, VM& vm, uint32_t capacity)
{
auto scope = DECLARE_THROW_SCOPE(vm);
size_t allocationSize = HashMapBuffer::allocationSize(capacity);
Expand Down Expand Up @@ -239,7 +239,7 @@ ALWAYS_INLINE uint32_t wangsInt64Hash(uint64_t key);
ALWAYS_INLINE uint32_t jsMapHash(JSBigInt*);
ALWAYS_INLINE uint32_t jsMapHash(JSGlobalObject*, VM&, JSValue);
ALWAYS_INLINE uint32_t shouldShrink(uint32_t capacity, uint32_t keyCount);
ALWAYS_INLINE uint32_t shouldRehashAfterAdd(uint32_t capacity, uint32_t keyCount, uint32_t deleteCount);
ALWAYS_INLINE uint32_t shouldRehash(uint32_t capacity, uint32_t keyCount, uint32_t deleteCount);
ALWAYS_INLINE uint32_t nextCapacity(uint32_t capacity, uint32_t keyCount);

template <typename HashMapBucketType>
Expand All @@ -256,28 +256,15 @@ class HashMapImpl : public JSNonFinalObject {

HashMapImpl(VM& vm, Structure* structure)
: Base(vm, structure)
, m_keyCount(0)
, m_deleteCount(0)
, m_capacity(4)
{
}

HashMapImpl(VM& vm, Structure* structure, uint32_t sizeHint)
: Base(vm, structure)
, m_keyCount(0)
, m_deleteCount(0)
{
uint32_t capacity = (Checked<uint32_t>(sizeHint) * 2) + 1;
capacity = std::max<uint32_t>(WTF::roundUpToPowerOfTwo(capacity), 4U);
m_capacity = capacity;
}

ALWAYS_INLINE HashMapBucketType** buffer() const
{
return m_buffer->buffer();
}

void finishCreation(JSGlobalObject*, VM&);
void finishCreation(VM&);
void finishCreation(JSGlobalObject*, VM&, HashMapImpl* base);

static HashMapBucketType* emptyValue()
Expand Down Expand Up @@ -320,7 +307,7 @@ class HashMapImpl : public JSNonFinalObject {
return m_keyCount;
}

ALWAYS_INLINE void clear(JSGlobalObject*);
ALWAYS_INLINE void clear(VM&);

ALWAYS_INLINE size_t bufferSizeInBytes() const
{
Expand Down Expand Up @@ -355,42 +342,39 @@ class HashMapImpl : public JSNonFinalObject {
}

private:
ALWAYS_INLINE uint32_t shouldRehashAfterAdd() const
{
return JSC::shouldRehashAfterAdd(m_capacity, m_keyCount, m_deleteCount);
}

ALWAYS_INLINE uint32_t shouldShrink() const
{
return JSC::shouldShrink(m_capacity, m_keyCount);
}

ALWAYS_INLINE void setUpHeadAndTail(JSGlobalObject*, VM&);
ALWAYS_INLINE void setUpHeadAndTail(VM&);

ALWAYS_INLINE void addNormalizedNonExistingForCloning(JSGlobalObject*, JSValue key, JSValue = JSValue());
ALWAYS_INLINE HashMapBucketType* addNormalizedNonExistingForCloningInternal(JSGlobalObject*, JSValue key, JSValue, uint32_t hash);

template<typename CanUseBucket>
ALWAYS_INLINE void addNormalizedInternal(JSGlobalObject*, JSValue key, JSValue, const CanUseBucket&);

template<typename CanUseBucket>
ALWAYS_INLINE HashMapBucketType* addNormalizedInternal(VM&, JSValue key, JSValue, uint32_t hash, const CanUseBucket&);
ALWAYS_INLINE HashMapBucketType* addNormalizedInternal(JSGlobalObject*, JSValue key, JSValue, uint32_t hash, const CanUseBucket&);

ALWAYS_INLINE HashMapBucketType** findBucketAlreadyHashedAndNormalized(JSGlobalObject*, JSValue key, uint32_t hash);

void rehash(JSGlobalObject*);
enum class RehashMode { BeforeAddition, AfterRemoval };
void rehash(JSGlobalObject*, RehashMode);

ALWAYS_INLINE void checkConsistency() const;

void makeAndSetNewBuffer(JSGlobalObject*, VM&);
void makeAndSetNewBuffer(JSGlobalObject*, uint32_t newCapacity, VM&);

ALWAYS_INLINE void assertBufferIsEmpty() const;
ALWAYS_INLINE static void assertBufferIsEmpty(HashMapBucketType**, uint32_t capacity);

WriteBarrier<HashMapBucketType> m_head;
WriteBarrier<HashMapBucketType> m_tail;
AuxiliaryBarrier<HashMapBufferType*> m_buffer;
uint32_t m_keyCount;
uint32_t m_deleteCount;
uint32_t m_capacity;
uint32_t m_keyCount { 0 };
uint32_t m_deleteCount { 0 };
uint32_t m_capacity { 0 };
};

} // namespace JSC
Loading

0 comments on commit e4b5df4

Please sign in to comment.