Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JSC] Make JSMap and JSSet construction more simple and efficient #3023

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions JSTests/stress/map-clear-get.js
@@ -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
@@ -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
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
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
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
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