Diff
Modified: trunk/JSTests/ChangeLog (267372 => 267373)
--- trunk/JSTests/ChangeLog 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/JSTests/ChangeLog 2020-09-21 22:10:24 UTC (rev 267373)
@@ -1,5 +1,25 @@
2020-09-21 Yusuke Suzuki <[email protected]>
+ [JSC] BigInt should work with Map / Set
+ https://bugs.webkit.org/show_bug.cgi?id=216667
+ <rdar://problem/69107221>
+
+ Reviewed by Robin Morisset.
+
+ * stress/bigint-and-map-set.js: Added.
+ (shouldBe):
+ (opaque1n):
+ (testMap):
+ (let.set new):
+ * stress/bigint-string-map-set.js: Added.
+ (shouldBe):
+ (testMap):
+ * stress/bigint32-map-set.js: Added.
+ (shouldBe):
+ (testMap):
+
+2020-09-21 Yusuke Suzuki <[email protected]>
+
[JSC] Proxy should be trapped if base value is primitive
https://bugs.webkit.org/show_bug.cgi?id=216764
Added: trunk/JSTests/stress/bigint-and-map-set.js (0 => 267373)
--- trunk/JSTests/stress/bigint-and-map-set.js (rev 0)
+++ trunk/JSTests/stress/bigint-and-map-set.js 2020-09-21 22:10:24 UTC (rev 267373)
@@ -0,0 +1,44 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function opaque1n()
+{
+ return 1n;
+}
+noInline(opaque1n);
+
+function testMap(map) {
+ map.set(1n, 42);
+ shouldBe(map.has(1n), true);
+ shouldBe(map.has(opaque1n()), true);
+ shouldBe(map.has(createHeapBigInt(opaque1n())), true);
+ shouldBe(map.get(1n), 42);
+ shouldBe(map.get(opaque1n()), 42);
+ shouldBe(map.get(createHeapBigInt(opaque1n())), 42);
+ map.set(1n, 40);
+ shouldBe(map.get(1n), 40);
+ shouldBe(map.get(opaque1n()), 40);
+ shouldBe(map.get(createHeapBigInt(opaque1n())), 40);
+}
+noInline(testMap);
+
+function testSet(set) {
+ set.add(1n);
+ shouldBe(set.has(1n), true);
+ shouldBe(set.has(opaque1n()), true);
+ shouldBe(set.has(createHeapBigInt(opaque1n())), true);
+ set.delete(createHeapBigInt(opaque1n()));
+ shouldBe(set.has(1n), false);
+ shouldBe(set.has(opaque1n()), false);
+ shouldBe(set.has(createHeapBigInt(opaque1n())), false);
+}
+noInline(testSet);
+
+let map = new Map();
+let set = new Set();
+for (let i = 0; i < 1e4; ++i) {
+ testMap(map);
+ testSet(set);
+}
Added: trunk/JSTests/stress/bigint-string-map-set.js (0 => 267373)
--- trunk/JSTests/stress/bigint-string-map-set.js (rev 0)
+++ trunk/JSTests/stress/bigint-string-map-set.js 2020-09-21 22:10:24 UTC (rev 267373)
@@ -0,0 +1,77 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function testMap(map, key)
+{
+ return map.has(key);
+}
+noInline(testMap);
+
+function testSet(set, key)
+{
+ return set.has(key);
+}
+noInline(testSet);
+
+let map = new Map();
+map.set("Hey", "Hey");
+map.set(null, null);
+map.set(1n, 1n);
+map.set(2n, 2n);
+map.set(0xffffffffffffffffn, 0xffffffffffffffffn);
+map.set("Hello", "Hello");
+
+let set = new Set();
+set.add("Hey");
+set.add(null);
+set.add(1n);
+set.add(2n);
+set.add(0xffffffffffffffffn);
+set.add("Hello");
+
+// String
+for (let i = 0; i < 1e4; ++i) {
+ shouldBe(testMap(map, "Hey"), true);
+ shouldBe(testSet(set, "Hey"), true);
+ shouldBe(testMap(map, "Hey1"), false);
+ shouldBe(testSet(set, "Hey1"), false);
+ shouldBe(testMap(map, "Hello"), true);
+ shouldBe(testSet(set, "Hello"), true);
+}
+
+// Cell
+for (let i = 0; i < 1e4; ++i) {
+ shouldBe(testMap(map, "Hey"), true);
+ shouldBe(testSet(set, "Hey"), true);
+ shouldBe(testMap(map, "Hey1"), false);
+ shouldBe(testSet(set, "Hey1"), false);
+ shouldBe(testMap(map, "Hello"), true);
+ shouldBe(testSet(set, "Hello"), true);
+ shouldBe(testMap(map, createHeapBigInt(0xffffffffffffffffn)), true);
+ shouldBe(testSet(set, createHeapBigInt(0xffffffffffffffffn)), true);
+ shouldBe(testMap(map, createHeapBigInt(0x1ffffffffffffffffn)), false);
+ shouldBe(testSet(set, createHeapBigInt(0x1ffffffffffffffffn)), false);
+}
+
+for (let i = 0; i < 1e4; ++i) {
+ shouldBe(testMap(map, "Hey"), true);
+ shouldBe(testSet(set, "Hey"), true);
+ shouldBe(testMap(map, "Hey1"), false);
+ shouldBe(testSet(set, "Hey1"), false);
+ shouldBe(testMap(map, "Hello"), true);
+ shouldBe(testSet(set, "Hello"), true);
+ shouldBe(testMap(map, createHeapBigInt(0xffffffffffffffffn)), true);
+ shouldBe(testSet(set, createHeapBigInt(0xffffffffffffffffn)), true);
+ shouldBe(testMap(map, createHeapBigInt(0x1ffffffffffffffffn)), false);
+ shouldBe(testSet(set, createHeapBigInt(0x1ffffffffffffffffn)), false);
+ shouldBe(testMap(map, createHeapBigInt(1n)), true);
+ shouldBe(testSet(set, createHeapBigInt(1n)), true);
+ shouldBe(testMap(map, 1n), true);
+ shouldBe(testSet(set, 1n), true);
+ shouldBe(testMap(map, 2n), true);
+ shouldBe(testSet(set, 2n), true);
+ shouldBe(testMap(map, 3n), false);
+ shouldBe(testSet(set, 3n), false);
+}
Added: trunk/JSTests/stress/bigint32-map-set.js (0 => 267373)
--- trunk/JSTests/stress/bigint32-map-set.js (rev 0)
+++ trunk/JSTests/stress/bigint32-map-set.js 2020-09-21 22:10:24 UTC (rev 267373)
@@ -0,0 +1,41 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function testMap(map, key)
+{
+ return map.has(key);
+}
+noInline(testMap);
+
+function testSet(set, key)
+{
+ return set.has(key);
+}
+noInline(testSet);
+
+let map = new Map();
+map.set("Hey", "Hey");
+map.set(null, null);
+map.set(1n, 1n);
+map.set(2n, 2n);
+map.set(0xffffffffffffffffn, 0xffffffffffffffffn);
+map.set("Hello", "Hello");
+
+let set = new Set();
+set.add("Hey");
+set.add(null);
+set.add(1n);
+set.add(2n);
+set.add(0xffffffffffffffffn);
+set.add("Hello");
+
+for (let i = 0; i < 1e4; ++i) {
+ shouldBe(testMap(map, 1n), true);
+ shouldBe(testSet(set, 1n), true);
+ shouldBe(testMap(map, 2n), true);
+ shouldBe(testSet(set, 2n), true);
+ shouldBe(testMap(map, 3n), false);
+ shouldBe(testSet(set, 3n), false);
+}
Modified: trunk/Source/_javascript_Core/ChangeLog (267372 => 267373)
--- trunk/Source/_javascript_Core/ChangeLog 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/ChangeLog 2020-09-21 22:10:24 UTC (rev 267373)
@@ -1,3 +1,45 @@
+2020-09-21 Yusuke Suzuki <[email protected]>
+
+ [JSC] BigInt should work with Map / Set
+ https://bugs.webkit.org/show_bug.cgi?id=216667
+
+ Reviewed by Robin Morisset.
+
+ This patch makes BigInt supported in Map / Set.
+
+ 1. In NormalizeMapKey, we always attempt to convert HeapBigInt to BigInt32 (if supported). So we ensure that,
+ normalized BigInt has one unique form for BigInt32 range. This allows us to use hashing for BigInt32 bit pattern directly.
+ 2. In MapHash, for BigInt32, we directly has the JSValue bits. For HeapBigInt, we calculate hash via Hasher.
+ 3. In GetMapBucket, we consider HeapBigInt case correctly.
+
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ (JSC::DFG::FixupPhase::fixupNormalizeMapKey):
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileNormalizeMapKey):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileMapHash):
+ (JSC::FTL::DFG::LowerDFGToB3::compileNormalizeMapKey):
+ (JSC::FTL::DFG::LowerDFGToB3::compileGetMapBucket):
+ * runtime/HashMapImpl.h:
+ (JSC::normalizeMapKey):
+ (JSC::jsMapHash):
+ (JSC::concurrentJSMapHash):
+ * runtime/JSBigInt.cpp:
+ (JSC::JSBigInt::concurrentHash):
+ * runtime/JSBigInt.h:
+ (JSC::tryConvertToBigInt32):
+
2020-09-21 Mark Lam <[email protected]>
Move some LLInt globals into JSC::Config.
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (267372 => 267373)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2020-09-21 22:10:24 UTC (rev 267373)
@@ -1381,7 +1381,7 @@
break;
}
- SpeculatedType typeMaybeNormalized = (SpecFullNumber & ~SpecInt32Only);
+ SpeculatedType typeMaybeNormalized = (SpecFullNumber & ~SpecInt32Only) | SpecHeapBigInt;
if (!(forNode(node->child1()).m_type & typeMaybeNormalized)) {
m_state.setShouldTryConstantFolding(true);
forNode(node) = forNode(node->child1());
Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (267372 => 267373)
--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2020-09-21 22:10:24 UTC (rev 267373)
@@ -994,7 +994,7 @@
}
case NormalizeMapKey: {
- SpeculatedType typeMaybeNormalized = (SpecFullNumber & ~SpecInt32Only);
+ SpeculatedType typeMaybeNormalized = (SpecFullNumber & ~SpecInt32Only) | SpecHeapBigInt;
if (m_state.forNode(node->child1()).m_type & typeMaybeNormalized)
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (267372 => 267373)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2020-09-21 22:10:24 UTC (rev 267373)
@@ -164,7 +164,7 @@
case SuperSamplerBegin:
case SuperSamplerEnd:
case CPUIntrinsic:
- case NormalizeMapKey:
+ case NormalizeMapKey: // HeapBigInt => BigInt32 conversion does not involve GC.
case GetMapBucketHead:
case GetMapBucketNext:
case LoadKeyFromMapBucket:
@@ -517,6 +517,10 @@
case Int32Use:
case SymbolUse:
case ObjectUse:
+#if USE(BIGINT32)
+ case BigInt32Use:
+#endif
+ case HeapBigIntUse:
return false;
default:
// We might resolve a rope.
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (267372 => 267373)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2020-09-21 22:10:24 UTC (rev 267373)
@@ -2377,6 +2377,10 @@
fixEdge<BooleanUse>(node->child2());
else if (node->child2()->shouldSpeculateInt32())
fixEdge<Int32Use>(node->child2());
+#if USE(BIGINT32)
+ else if (node->child2()->shouldSpeculateBigInt32())
+ fixEdge<BigInt32Use>(node->child2());
+#endif
else if (node->child2()->shouldSpeculateSymbol())
fixEdge<SymbolUse>(node->child2());
else if (node->child2()->shouldSpeculateObject())
@@ -2383,6 +2387,8 @@
fixEdge<ObjectUse>(node->child2());
else if (node->child2()->shouldSpeculateString())
fixEdge<StringUse>(node->child2());
+ else if (node->child2()->shouldSpeculateHeapBigInt())
+ fixEdge<HeapBigIntUse>(node->child2());
else if (node->child2()->shouldSpeculateCell())
fixEdge<CellUse>(node->child2());
else
@@ -2436,6 +2442,18 @@
break;
}
+#if USE(BIGINT32)
+ if (node->child1()->shouldSpeculateBigInt32()) {
+ fixEdge<BigInt32Use>(node->child1());
+ break;
+ }
+#endif
+
+ if (node->child1()->shouldSpeculateHeapBigInt()) {
+ fixEdge<HeapBigIntUse>(node->child1());
+ break;
+ }
+
if (node->child1()->shouldSpeculateCell()) {
fixEdge<CellUse>(node->child1());
break;
@@ -3996,11 +4014,13 @@
return;
}
- if (node->child1()->shouldSpeculateCell()) {
- fixEdge<CellUse>(node->child1());
+#if USE(BIGINT32)
+ if (node->child1()->shouldSpeculateBigInt32()) {
+ fixEdge<BigInt32Use>(node->child1());
node->convertToIdentity();
return;
}
+#endif
fixEdge<UntypedUse>(node->child1());
}
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (267372 => 267373)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2020-09-21 22:10:24 UTC (rev 267373)
@@ -3212,6 +3212,14 @@
return putDynamicVar(globalObject, vm, scope, value, impl, getPutInfoBits, isStrictMode);
}
+EncodedJSValue JIT_OPERATION operationNormalizeMapKey(VM* vmPointer, EncodedJSValue input)
+{
+ VM& vm = *vmPointer;
+ CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+ JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
+ return JSValue::encode(normalizeMapKey(JSValue::decode(input)));
+}
+
UCPUStrictInt32 JIT_OPERATION operationMapHash(JSGlobalObject* globalObject, EncodedJSValue input)
{
VM& vm = globalObject->vm();
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (267372 => 267373)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.h 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h 2020-09-21 22:10:24 UTC (rev 267373)
@@ -243,6 +243,7 @@
char* JIT_OPERATION operationInt52ToStringWithValidRadix(JSGlobalObject*, int64_t, int32_t);
char* JIT_OPERATION operationDoubleToStringWithValidRadix(JSGlobalObject*, double, int32_t);
+EncodedJSValue JIT_OPERATION operationNormalizeMapKey(VM*, EncodedJSValue input) WTF_INTERNAL;
UCPUStrictInt32 JIT_OPERATION operationMapHash(JSGlobalObject*, EncodedJSValue input);
JSCell* JIT_OPERATION operationJSMapFindBucket(JSGlobalObject*, JSCell*, EncodedJSValue, int32_t);
JSCell* JIT_OPERATION operationJSSetFindBucket(JSGlobalObject*, JSCell*, EncodedJSValue, int32_t);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (267372 => 267373)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2020-09-21 22:10:24 UTC (rev 267373)
@@ -12517,6 +12517,11 @@
CCallHelpers::JumpList passThroughCases;
CCallHelpers::JumpList doneCases;
+ auto isNotCell = m_jit.branchIfNotCell(keyRegs);
+ passThroughCases.append(m_jit.branchIfNotHeapBigInt(keyRegs.payloadGPR()));
+ auto slowPath = m_jit.jump();
+ isNotCell.link(&m_jit);
+
passThroughCases.append(m_jit.branchIfNotNumber(keyRegs, scratchGPR));
passThroughCases.append(m_jit.branchIfInt32(keyRegs));
@@ -12539,6 +12544,7 @@
passThroughCases.link(&m_jit);
m_jit.moveValueRegs(keyRegs, resultRegs);
+ addSlowPathGenerator(slowPathCall(slowPath, this, operationNormalizeMapKey, resultRegs, &vm(), keyRegs));
doneCases.link(&m_jit);
jsValueResult(resultRegs, node);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (267372 => 267373)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2020-09-21 22:10:24 UTC (rev 267373)
@@ -4431,6 +4431,9 @@
case MapHash: {
switch (node->child1().useKind()) {
+#if USE(BIGINT32)
+ case BigInt32Use:
+#endif
case BooleanUse:
case Int32Use:
case SymbolUse:
@@ -4450,6 +4453,20 @@
strictInt32Result(resultGPR, node);
break;
}
+ case HeapBigIntUse: {
+ SpeculateCellOperand input(this, node->child1());
+ GPRReg inputGPR = input.gpr();
+
+ speculateHeapBigInt(node->child1(), inputGPR);
+
+ flushRegisters();
+ GPRFlushedCallResult result(this);
+ GPRReg resultGPR = result.gpr();
+ callOperation(operationMapHash, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), inputGPR);
+ m_jit.exceptionCheck();
+ strictInt32Result(resultGPR, node);
+ break;
+ }
case CellUse:
case StringUse: {
SpeculateCellOperand input(this, node->child1());
@@ -4472,8 +4489,10 @@
speculateString(node->child1(), inputGPR);
else {
auto isString = m_jit.branchIfString(inputGPR);
+ auto isHeapBigInt = m_jit.branchIfHeapBigInt(inputGPR);
m_jit.move(inputGPR, resultGPR);
m_jit.wangsInt64Hash(resultGPR, tempGPR);
+ addSlowPathGenerator(slowPathCall(isHeapBigInt, this, operationMapHash, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), inputGPR));
done.append(m_jit.jump());
isString.link(&m_jit);
}
@@ -4515,6 +4534,7 @@
MacroAssembler::JumpList done;
straightHash.append(m_jit.branchIfNotCell(inputGPR));
MacroAssembler::JumpList slowPath;
+ auto isHeapBigInt = m_jit.branchIfHeapBigInt(inputGPR);
straightHash.append(m_jit.branchIfNotString(inputGPR));
m_jit.loadPtr(MacroAssembler::Address(inputGPR, JSString::offsetOfValue()), resultGPR);
slowPath.append(m_jit.branchIfRopeStringImpl(resultGPR));
@@ -4526,6 +4546,7 @@
straightHash.link(&m_jit);
m_jit.move(inputGPR, resultGPR);
m_jit.wangsInt64Hash(resultGPR, tempGPR);
+ addSlowPathGenerator(slowPathCall(isHeapBigInt, this, operationMapHash, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), inputGPR));
done.append(m_jit.jump());
slowPath.link(&m_jit);
@@ -4596,6 +4617,9 @@
// Perform Object.is()
switch (node->child2().useKind()) {
case BooleanUse:
+#if USE(BIGINT32)
+ case BigInt32Use:
+#endif
case Int32Use:
case SymbolUse:
case ObjectUse: {
@@ -4604,11 +4628,26 @@
break;
}
case CellUse: {
+ // if (bucket.isString()) {
+ // if (key.isString())
+ // => slow path
+ // } else if (bucket.isHeapBigInt()) {
+ // if (key.isHeapBigInt())
+ // => slow path
+ // }
done.append(m_jit.branch64(MacroAssembler::Equal, bucketGPR, keyGPR));
loopAround.append(m_jit.branchIfNotCell(JSValueRegs(bucketGPR)));
- loopAround.append(m_jit.branchIfNotString(bucketGPR));
+
+ auto isBucketString = m_jit.branchIfString(bucketGPR);
+ loopAround.append(m_jit.branchIfNotHeapBigInt(bucketGPR));
+
+ // bucket is HeapBigInt.
+ slowPathCases.append(m_jit.branchIfHeapBigInt(keyGPR));
+ loopAround.append(m_jit.jump());
+
+ // bucket is String.
+ isBucketString.link(&m_jit);
loopAround.append(m_jit.branchIfNotString(keyGPR));
- // They're both strings.
slowPathCases.append(m_jit.jump());
break;
}
@@ -4619,6 +4658,13 @@
slowPathCases.append(m_jit.jump());
break;
}
+ case HeapBigIntUse: {
+ done.append(m_jit.branch64(MacroAssembler::Equal, bucketGPR, keyGPR)); // They're definitely the same value, we found the bucket we were looking for!
+ loopAround.append(m_jit.branchIfNotCell(JSValueRegs(bucketGPR)));
+ loopAround.append(m_jit.branchIfNotHeapBigInt(bucketGPR));
+ slowPathCases.append(m_jit.jump());
+ break;
+ }
case UntypedUse: {
done.append(m_jit.branch64(MacroAssembler::Equal, bucketGPR, keyGPR)); // They're definitely the same value, we found the bucket we were looking for!
// The input key and bucket's key are already normalized. So if 64-bit compare fails and one is not a cell, they're definitely not equal.
@@ -4626,11 +4672,16 @@
// first is a cell here.
loopAround.append(m_jit.branchIfNotCell(JSValueRegs(keyGPR)));
// Both are cells here.
- loopAround.append(m_jit.branchIfNotString(bucketGPR));
- // The first is a string here.
- slowPathCases.append(m_jit.branchIfString(keyGPR));
- // The first is a string, but the second is not, we continue to loop around.
+ auto isBucketString = m_jit.branchIfString(bucketGPR);
+ // bucket is not String.
+ loopAround.append(m_jit.branchIfNotHeapBigInt(bucketGPR));
+ // bucket is HeapBigInt.
+ slowPathCases.append(m_jit.branchIfHeapBigInt(keyGPR));
loopAround.append(m_jit.jump());
+ // bucket is String.
+ isBucketString.link(&m_jit);
+ loopAround.append(m_jit.branchIfNotString(keyGPR));
+ slowPathCases.append(m_jit.jump());
break;
}
default:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (267372 => 267373)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2020-09-21 22:10:24 UTC (rev 267373)
@@ -11244,6 +11244,9 @@
{
JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic);
switch (m_node->child1().useKind()) {
+#if USE(BIGINT32)
+ case BigInt32Use:
+#endif
case BooleanUse:
case Int32Use:
case SymbolUse:
@@ -11254,9 +11257,17 @@
return;
}
+ case HeapBigIntUse: {
+ LValue key = lowHeapBigInt(m_node->child1());
+ setInt32(m_out.castToInt32(vmCall(Int64, operationMapHash, weakPointer(globalObject), key)));
+ return;
+ }
+
case CellUse: {
LBasicBlock isString = m_out.newBlock();
LBasicBlock notString = m_out.newBlock();
+ LBasicBlock isHeapBigIntCase = m_out.newBlock();
+ LBasicBlock notStringHeapBigIntCase = m_out.newBlock();
LBasicBlock continuation = m_out.newBlock();
LValue value = lowCell(m_node->child1());
@@ -11268,12 +11279,19 @@
ValueFromBlock stringResult = m_out.anchor(mapHashString(value, m_node->child1()));
m_out.jump(continuation);
- m_out.appendTo(notString, continuation);
+ m_out.appendTo(notString, isHeapBigIntCase);
+ m_out.branch(isHeapBigInt(value, (provenType(m_node->child1()) & ~SpecString)), unsure(isHeapBigIntCase), unsure(notStringHeapBigIntCase));
+
+ m_out.appendTo(isHeapBigIntCase, notStringHeapBigIntCase);
+ ValueFromBlock heapBigIntResult = m_out.anchor(m_out.castToInt32(vmCall(Int64, operationMapHash, weakPointer(globalObject), value)));
+ m_out.jump(continuation);
+
+ m_out.appendTo(notStringHeapBigIntCase, continuation);
ValueFromBlock notStringResult = m_out.anchor(wangsInt64Hash(value));
m_out.jump(continuation);
m_out.appendTo(continuation, lastNext);
- setInt32(m_out.phi(Int32, stringResult, notStringResult));
+ setInt32(m_out.phi(Int32, stringResult, heapBigIntResult, notStringResult));
return;
}
@@ -11294,6 +11312,7 @@
LBasicBlock slowCase = m_out.newBlock();
LBasicBlock straightHash = m_out.newBlock();
LBasicBlock isStringCase = m_out.newBlock();
+ LBasicBlock notStringCase = m_out.newBlock();
LBasicBlock nonEmptyStringCase = m_out.newBlock();
LBasicBlock continuation = m_out.newBlock();
@@ -11300,11 +11319,14 @@
m_out.branch(
isCell(value, provenType(m_node->child1())), unsure(isCellCase), unsure(straightHash));
- LBasicBlock lastNext = m_out.appendTo(isCellCase, isStringCase);
+ LBasicBlock lastNext = m_out.appendTo(isCellCase, notStringCase);
LValue isString = m_out.equal(m_out.load8ZeroExt32(value, m_heaps.JSCell_typeInfoType), m_out.constInt32(StringType));
m_out.branch(
- isString, unsure(isStringCase), unsure(straightHash));
+ isString, unsure(isStringCase), unsure(notStringCase));
+ m_out.appendTo(notStringCase, isStringCase);
+ m_out.branch(isHeapBigInt(value, (provenType(m_node->child1()) & ~SpecString)), unsure(slowCase), unsure(straightHash));
+
m_out.appendTo(isStringCase, nonEmptyStringCase);
m_out.branch(isRopeString(value, m_node->child1()), rarely(slowCase), usually(nonEmptyStringCase));
@@ -11331,6 +11353,9 @@
{
ASSERT(m_node->child1().useKind() == UntypedUse);
+ LBasicBlock isCellCase = m_out.newBlock();
+ LBasicBlock notCellCase = m_out.newBlock();
+ LBasicBlock isHeapBigIntCase = m_out.newBlock();
LBasicBlock isNumberCase = m_out.newBlock();
LBasicBlock notInt32NumberCase = m_out.newBlock();
LBasicBlock notNaNCase = m_out.newBlock();
@@ -11337,10 +11362,20 @@
LBasicBlock convertibleCase = m_out.newBlock();
LBasicBlock continuation = m_out.newBlock();
- LBasicBlock lastNext = m_out.insertNewBlocksBefore(isNumberCase);
+ LBasicBlock lastNext = m_out.insertNewBlocksBefore(isCellCase);
LValue key = lowJSValue(m_node->child1());
ValueFromBlock fastResult = m_out.anchor(key);
+ m_out.branch(isNotCell(key, provenType(m_node->child1())), unsure(notCellCase), unsure(isCellCase));
+
+ m_out.appendTo(isCellCase, isHeapBigIntCase);
+ m_out.branch(isNotHeapBigInt(key, (provenType(m_node->child1()) & SpecCellCheck)), unsure(continuation), unsure(isHeapBigIntCase));
+
+ m_out.appendTo(isHeapBigIntCase, notCellCase);
+ ValueFromBlock bigIntResult = m_out.anchor(vmCall(Int64, operationNormalizeMapKey, m_vmValue, key));
+ m_out.jump(continuation);
+
+ m_out.appendTo(notCellCase, isNumberCase);
m_out.branch(isNotNumber(key), unsure(continuation), unsure(isNumberCase));
m_out.appendTo(isNumberCase, notInt32NumberCase);
@@ -11362,7 +11397,7 @@
m_out.jump(continuation);
m_out.appendTo(continuation, lastNext);
- setJSValue(m_out.phi(Int64, fastResult, normalizedNaNResult, doubleResult, boxedIntResult));
+ setJSValue(m_out.phi(Int64, fastResult, bigIntResult, normalizedNaNResult, doubleResult, boxedIntResult));
}
void compileGetMapBucket()
@@ -11418,6 +11453,9 @@
// Perform Object.is()
switch (m_node->child2().useKind()) {
case BooleanUse:
+#if USE(BIGINT32)
+ case BigInt32Use:
+#endif
case Int32Use:
case SymbolUse:
case ObjectUse: {
@@ -11441,10 +11479,28 @@
unsure(slowPath), unsure(loopAround));
break;
}
+ case HeapBigIntUse: {
+ LBasicBlock notBitEqual = m_out.newBlock();
+ LBasicBlock bucketKeyIsCell = m_out.newBlock();
+
+ m_out.branch(m_out.equal(key, bucketKey),
+ unsure(continuation), unsure(notBitEqual));
+
+ m_out.appendTo(notBitEqual, bucketKeyIsCell);
+ m_out.branch(isCell(bucketKey),
+ unsure(bucketKeyIsCell), unsure(loopAround));
+
+ m_out.appendTo(bucketKeyIsCell, loopAround);
+ m_out.branch(isHeapBigInt(bucketKey),
+ unsure(slowPath), unsure(loopAround));
+ break;
+ }
case CellUse: {
LBasicBlock notBitEqual = m_out.newBlock();
LBasicBlock bucketKeyIsCell = m_out.newBlock();
LBasicBlock bucketKeyIsString = m_out.newBlock();
+ LBasicBlock bucketKeyIsNotString = m_out.newBlock();
+ LBasicBlock bucketKeyIsHeapBigInt = m_out.newBlock();
m_out.branch(m_out.equal(key, bucketKey),
unsure(continuation), unsure(notBitEqual));
@@ -11455,11 +11511,19 @@
m_out.appendTo(bucketKeyIsCell, bucketKeyIsString);
m_out.branch(isString(bucketKey),
- unsure(bucketKeyIsString), unsure(loopAround));
+ unsure(bucketKeyIsString), unsure(bucketKeyIsNotString));
- m_out.appendTo(bucketKeyIsString, loopAround);
- m_out.branch(isString(key),
+ m_out.appendTo(bucketKeyIsString, bucketKeyIsNotString);
+ m_out.branch(isString(key, provenType(m_node->child2())),
unsure(slowPath), unsure(loopAround));
+
+ m_out.appendTo(bucketKeyIsNotString, bucketKeyIsHeapBigInt);
+ m_out.branch(isHeapBigInt(bucketKey),
+ unsure(bucketKeyIsHeapBigInt), unsure(loopAround));
+
+ m_out.appendTo(bucketKeyIsHeapBigInt, loopAround);
+ m_out.branch(isHeapBigInt(key, provenType(m_node->child2())),
+ unsure(slowPath), unsure(loopAround));
break;
}
case UntypedUse: {
@@ -11467,6 +11531,8 @@
LBasicBlock bucketKeyIsCell = m_out.newBlock();
LBasicBlock bothAreCells = m_out.newBlock();
LBasicBlock bucketKeyIsString = m_out.newBlock();
+ LBasicBlock bucketKeyIsNotString = m_out.newBlock();
+ LBasicBlock bucketKeyIsHeapBigInt = m_out.newBlock();
m_out.branch(m_out.equal(key, bucketKey),
unsure(continuation), unsure(notBitEqual));
@@ -11481,11 +11547,19 @@
m_out.appendTo(bothAreCells, bucketKeyIsString);
m_out.branch(isString(bucketKey),
- unsure(bucketKeyIsString), unsure(loopAround));
+ unsure(bucketKeyIsString), unsure(bucketKeyIsNotString));
- m_out.appendTo(bucketKeyIsString, loopAround);
- m_out.branch(isString(key),
+ m_out.appendTo(bucketKeyIsString, bucketKeyIsNotString);
+ m_out.branch(isString(key, provenType(m_node->child2())),
unsure(slowPath), unsure(loopAround));
+
+ m_out.appendTo(bucketKeyIsNotString, bucketKeyIsHeapBigInt);
+ m_out.branch(isHeapBigInt(bucketKey),
+ unsure(bucketKeyIsHeapBigInt), unsure(loopAround));
+
+ m_out.appendTo(bucketKeyIsHeapBigInt, loopAround);
+ m_out.branch(isHeapBigInt(key, provenType(m_node->child2())),
+ unsure(slowPath), unsure(loopAround));
break;
}
default:
Modified: trunk/Source/_javascript_Core/runtime/HashMapImpl.h (267372 => 267373)
--- trunk/Source/_javascript_Core/runtime/HashMapImpl.h 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/runtime/HashMapImpl.h 2020-09-21 22:10:24 UTC (rev 267373)
@@ -243,8 +243,11 @@
// Keep in sync with the implementation of DFG and FTL normalization.
ALWAYS_INLINE JSValue normalizeMapKey(JSValue key)
{
- if (!key.isNumber())
+ if (!key.isNumber()) {
+ if (key.isHeapBigInt())
+ return tryConvertToBigInt32(key.asHeapBigInt());
return key;
+ }
if (key.isInt32())
return key;
@@ -287,6 +290,9 @@
return wtfString.impl()->hash();
}
+ if (value.isHeapBigInt())
+ return value.asHeapBigInt()->hash();
+
return wangsInt64Hash(JSValue::encode(value));
}
@@ -303,6 +309,9 @@
return impl->concurrentHash();
}
+ if (key.isHeapBigInt())
+ return key.asHeapBigInt()->concurrentHash();
+
uint64_t rawValue = JSValue::encode(key);
return wangsInt64Hash(rawValue);
}
Modified: trunk/Source/_javascript_Core/runtime/JSBigInt.cpp (267372 => 267373)
--- trunk/Source/_javascript_Core/runtime/JSBigInt.cpp 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/runtime/JSBigInt.cpp 2020-09-21 22:10:24 UTC (rev 267373)
@@ -54,6 +54,7 @@
#include "ParseInt.h"
#include "StructureInlines.h"
#include <algorithm>
+#include <wtf/Hasher.h>
#include <wtf/MathExtras.h>
namespace JSC {
@@ -430,31 +431,6 @@
: payload(value)
{ }
-static ALWAYS_INLINE JSValue tryConvertToBigInt32(JSBigInt* bigInt)
-{
-#if USE(BIGINT32)
- if (UNLIKELY(!bigInt))
- return JSValue();
-
- if (bigInt->length() <= 1) {
- if (!bigInt->length())
- return jsBigInt32(0);
- JSBigInt::Digit digit = bigInt->digit(0);
- if (bigInt->sign()) {
- static constexpr uint64_t maxValue = -static_cast<int64_t>(std::numeric_limits<int32_t>::min());
- if (digit <= maxValue)
- return jsBigInt32(static_cast<int32_t>(-static_cast<int64_t>(digit)));
- } else {
- static constexpr uint64_t maxValue = static_cast<uint64_t>(std::numeric_limits<int32_t>::max());
- if (digit <= maxValue)
- return jsBigInt32(static_cast<int32_t>(digit));
- }
- }
-#endif
-
- return bigInt;
-}
-
static ALWAYS_INLINE JSValue tryConvertToBigInt32(JSBigInt::ImplResult implResult)
{
if (!implResult.payload)
@@ -3073,4 +3049,27 @@
}
#endif
+static ALWAYS_INLINE unsigned computeHash(JSBigInt::Digit* digits, unsigned length, bool sign)
+{
+ Hasher hasher;
+ WTF::add(hasher, sign);
+ for (unsigned index = 0; index < length; ++index)
+ WTF::add(hasher, digits[index]);
+ return hasher.hash();
+}
+
+Optional<unsigned> JSBigInt::concurrentHash()
+{
+ // FIXME: Implement JSBigInt::concurrentHash by inserting right store barriers.
+ // https://bugs.webkit.org/show_bug.cgi?id=216801
+ return WTF::nullopt;
+}
+
+unsigned JSBigInt::hashSlow()
+{
+ ASSERT(!m_hash);
+ m_hash = computeHash(dataStorage(), length(), m_sign);
+ return m_hash;
+}
+
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/JSBigInt.h (267372 => 267373)
--- trunk/Source/_javascript_Core/runtime/JSBigInt.h 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/_javascript_Core/runtime/JSBigInt.h 2020-09-21 22:10:24 UTC (rev 267373)
@@ -427,11 +427,21 @@
JS_EXPORT_PRIVATE JSBigInt* rightTrim(JSGlobalObject*);
JS_EXPORT_PRIVATE JSBigInt* tryRightTrim(VM&);
+ JS_EXPORT_PRIVATE Optional<unsigned> concurrentHash();
+ unsigned hash()
+ {
+ if (m_hash)
+ return m_hash;
+ return hashSlow();
+ }
+
private:
JSBigInt(VM&, Structure*, Digit*, unsigned length);
JSBigInt* rightTrim(JSGlobalObject*, VM&);
+ JS_EXPORT_PRIVATE unsigned hashSlow();
+
static JSBigInt* createFromImpl(JSGlobalObject*, uint64_t value, bool sign);
static constexpr unsigned bitsPerByte = 8;
@@ -572,8 +582,10 @@
}
inline Digit* dataStorage() { return m_data.get(m_length); }
+ inline Digit* dataStorageUnsafe() { return m_data.getUnsafe(); }
const unsigned m_length;
+ unsigned m_hash { 0 };
bool m_sign { false };
CagedBarrierPtr<Gigacage::Primitive, Digit, tagCagedPtr> m_data;
};
@@ -608,4 +620,29 @@
}
}
+ALWAYS_INLINE JSValue tryConvertToBigInt32(JSBigInt* bigInt)
+{
+#if USE(BIGINT32)
+ if (UNLIKELY(!bigInt))
+ return JSValue();
+
+ if (bigInt->length() <= 1) {
+ if (!bigInt->length())
+ return jsBigInt32(0);
+ JSBigInt::Digit digit = bigInt->digit(0);
+ if (bigInt->sign()) {
+ static constexpr uint64_t maxValue = -static_cast<int64_t>(std::numeric_limits<int32_t>::min());
+ if (digit <= maxValue)
+ return jsBigInt32(static_cast<int32_t>(-static_cast<int64_t>(digit)));
+ } else {
+ static constexpr uint64_t maxValue = static_cast<uint64_t>(std::numeric_limits<int32_t>::max());
+ if (digit <= maxValue)
+ return jsBigInt32(static_cast<int32_t>(digit));
+ }
+ }
+#endif
+
+ return bigInt;
+}
+
} // namespace JSC
Modified: trunk/Source/WTF/ChangeLog (267372 => 267373)
--- trunk/Source/WTF/ChangeLog 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/WTF/ChangeLog 2020-09-21 22:10:24 UTC (rev 267373)
@@ -1,3 +1,14 @@
+2020-09-21 Yusuke Suzuki <[email protected]>
+
+ [JSC] BigInt should work with Map / Set
+ https://bugs.webkit.org/show_bug.cgi?id=216667
+
+ Reviewed by Robin Morisset.
+
+ * wtf/Hasher.h:
+ (WTF::Hasher::hash const):
+ (WTF::add):
+
2020-09-21 Mark Lam <[email protected]>
Move some LLInt globals into JSC::Config.
Modified: trunk/Source/WTF/wtf/Hasher.h (267372 => 267373)
--- trunk/Source/WTF/wtf/Hasher.h 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/WTF/wtf/Hasher.h 2020-09-21 22:10:24 UTC (rev 267373)
@@ -47,6 +47,7 @@
template<typename... Types> uint32_t computeHash(const Types&...);
template<typename T, typename... OtherTypes> uint32_t computeHash(std::initializer_list<T>, std::initializer_list<OtherTypes>...);
+template<typename UnsignedInteger> std::enable_if_t<std::is_unsigned<UnsignedInteger>::value && sizeof(UnsignedInteger) <= sizeof(uint32_t), void> add(Hasher&, UnsignedInteger);
class Hasher {
WTF_MAKE_FAST_ALLOCATED;
@@ -75,6 +76,11 @@
hasher.m_underlyingHasher.addCharactersAssumingAligned(sizedInteger, sizedInteger >> 16);
}
+ unsigned hash() const
+ {
+ return m_underlyingHasher.hash();
+ }
+
private:
StringHasher m_underlyingHasher;
};
@@ -91,6 +97,11 @@
add(hasher, static_cast<std::make_unsigned_t<SignedArithmetic>>(number));
}
+inline void add(Hasher& hasher, bool boolean)
+{
+ add(hasher, static_cast<uint8_t>(boolean));
+}
+
inline void add(Hasher& hasher, double number)
{
add(hasher, bitwise_cast<uint64_t>(number));
Modified: trunk/Source/WebCore/ChangeLog (267372 => 267373)
--- trunk/Source/WebCore/ChangeLog 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/WebCore/ChangeLog 2020-09-21 22:10:24 UTC (rev 267373)
@@ -1,3 +1,16 @@
+2020-09-21 Yusuke Suzuki <[email protected]>
+
+ [JSC] BigInt should work with Map / Set
+ https://bugs.webkit.org/show_bug.cgi?id=216667
+ <rdar://problem/69107221>
+
+ Reviewed by Robin Morisset.
+
+ Strongly ensure that BigInt32 is always selected since Map / Set could use it as a key.
+
+ * bindings/js/SerializedScriptValue.cpp:
+ (WebCore::CloneDeserializer::readBigInt):
+
2020-09-21 Peng Liu <[email protected]>
Tapping to zoom in and out causes video to become very small on some iPhone models
Modified: trunk/Source/WebCore/bindings/js/SerializedScriptValue.cpp (267372 => 267373)
--- trunk/Source/WebCore/bindings/js/SerializedScriptValue.cpp 2020-09-21 22:03:44 UTC (rev 267372)
+++ trunk/Source/WebCore/bindings/js/SerializedScriptValue.cpp 2020-09-21 22:10:24 UTC (rev 267373)
@@ -3054,7 +3054,7 @@
return JSValue();
}
m_gcBuffer.appendWithCrashOnOverflow(bigInt);
- return bigInt;
+ return tryConvertToBigInt32(bigInt);
}
#endif
JSBigInt* bigInt = nullptr;
@@ -3094,7 +3094,7 @@
return JSValue();
}
m_gcBuffer.appendWithCrashOnOverflow(bigInt);
- return bigInt;
+ return tryConvertToBigInt32(bigInt);
}
JSValue readTerminal()