From 67656a7d07d9c8e25e63056eec9af0d9531ef027 Mon Sep 17 00:00:00 2001 From: "utatane.tea@gmail.com" Date: Tue, 21 Nov 2017 10:40:34 +0000 Subject: [PATCH] [DFG][FTL] Support MapSet / SetAdd intrinsics https://bugs.webkit.org/show_bug.cgi?id=179858 Reviewed by Saam Barati. JSTests: * microbenchmarks/map-has-and-set.js: Added. (test): * stress/map-set-check-failure.js: Added. (shouldBe): (shouldThrow): (target): * stress/map-set-cse.js: Added. (shouldBe): (test): * stress/set-add-check-failure.js: Added. (shouldBe): (shouldThrow): (set shouldThrow): * stress/set-add-cse.js: Added. (shouldBe): Source/JavaScriptCore: Map.prototype.set and Set.prototype.add uses MapHash value anyway. By handling them as MapSet and SetAdd DFG nodes and decoupling MapSet and SetAdd nodes from MapHash DFG node, we have a chance to remove duplicate MapHash calculation for the same key. One story is *set-if-not-exists*. if (!map.has(key)) map.set(key, value); In the above code, both `has` and `set` require hash value for `key`. If we can change `set` to the series of DFG nodes: 1: MapHash(key) 2: MapSet(MapObjectUse:map, Untyped:key, Untyped:value, Int32Use:@1) we can remove duplicate @1 produced by `has` operation. This patch improves SixSpeed map-set.es6 and map-set-object.es6 by 20.5% and 20.4% respectively, baseline patched map-set.es6 246.2413+-15.2084 ^ 204.3679+-11.2408 ^ definitely 1.2049x faster map-set-object.es6 266.5075+-17.2289 ^ 221.2792+-12.2948 ^ definitely 1.2044x faster Microbenchmarks map-has-and-set 148.1522+-7.6665 ^ 131.4552+-7.8846 ^ definitely 1.1270x faster * dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter::executeEffects): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::handleIntrinsicCall): * dfg/DFGClobberize.h: (JSC::DFG::clobberize): * dfg/DFGDoesGC.cpp: (JSC::DFG::doesGC): * dfg/DFGFixupPhase.cpp: (JSC::DFG::FixupPhase::fixupNode): * dfg/DFGNodeType.h: * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGPredictionPropagationPhase.cpp: * dfg/DFGSafeToExecute.h: (JSC::DFG::safeToExecute): * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileSetAdd): (JSC::DFG::SpeculativeJIT::compileMapSet): * dfg/DFGSpeculativeJIT.h: (JSC::DFG::SpeculativeJIT::callOperation): * dfg/DFGSpeculativeJIT32_64.cpp: (JSC::DFG::SpeculativeJIT::compile): * dfg/DFGSpeculativeJIT64.cpp: (JSC::DFG::SpeculativeJIT::compile): * ftl/FTLCapabilities.cpp: (JSC::FTL::canCompile): * ftl/FTLLowerDFGToB3.cpp: (JSC::FTL::DFG::LowerDFGToB3::compileNode): (JSC::FTL::DFG::LowerDFGToB3::compileSetAdd): (JSC::FTL::DFG::LowerDFGToB3::compileMapSet): * jit/JITOperations.h: * runtime/HashMapImpl.h: (JSC::HashMapImpl::addNormalized): (JSC::HashMapImpl::addNormalizedInternal): * runtime/Intrinsic.cpp: (JSC::intrinsicName): * runtime/Intrinsic.h: * runtime/MapPrototype.cpp: (JSC::MapPrototype::finishCreation): * runtime/SetPrototype.cpp: (JSC::SetPrototype::finishCreation): git-svn-id: https://svn.webkit.org/repository/webkit/trunk@225072 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- JSTests/ChangeLog | 23 +++++++ JSTests/microbenchmarks/map-has-and-set.js | 12 ++++ JSTests/stress/map-set-check-failure.js | 35 ++++++++++ JSTests/stress/map-set-cse.js | 21 ++++++ JSTests/stress/set-add-check-failure.js | 35 ++++++++++ JSTests/stress/set-add-cse.js | 21 ++++++ Source/JavaScriptCore/ChangeLog | 79 ++++++++++++++++++++++ .../dfg/DFGAbstractInterpreterInlines.h | 4 ++ Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp | 32 +++++++++ Source/JavaScriptCore/dfg/DFGClobberize.h | 8 +++ Source/JavaScriptCore/dfg/DFGDoesGC.cpp | 2 + Source/JavaScriptCore/dfg/DFGFixupPhase.cpp | 12 ++++ Source/JavaScriptCore/dfg/DFGNodeType.h | 2 + Source/JavaScriptCore/dfg/DFGOperations.cpp | 16 +++++ Source/JavaScriptCore/dfg/DFGOperations.h | 2 + .../dfg/DFGPredictionPropagationPhase.cpp | 2 + Source/JavaScriptCore/dfg/DFGSafeToExecute.h | 4 ++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp | 38 +++++++++++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h | 22 ++++++ .../JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp | 8 +++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp | 8 +++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp | 2 + Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp | 25 +++++++ Source/JavaScriptCore/jit/JITOperations.h | 2 + Source/JavaScriptCore/runtime/HashMapImpl.h | 28 ++++++-- Source/JavaScriptCore/runtime/Intrinsic.cpp | 4 ++ Source/JavaScriptCore/runtime/Intrinsic.h | 2 + Source/JavaScriptCore/runtime/MapPrototype.cpp | 4 +- Source/JavaScriptCore/runtime/SetPrototype.cpp | 4 +- 29 files changed, 449 insertions(+), 8 deletions(-) create mode 100644 JSTests/microbenchmarks/map-has-and-set.js create mode 100644 JSTests/stress/map-set-check-failure.js create mode 100644 JSTests/stress/map-set-cse.js create mode 100644 JSTests/stress/set-add-check-failure.js create mode 100644 JSTests/stress/set-add-cse.js diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog index 96bf4cf..e636656 100644 --- a/JSTests/ChangeLog +++ b/JSTests/ChangeLog @@ -1,5 +1,28 @@ 2017-11-21 Yusuke Suzuki + [DFG][FTL] Support MapSet / SetAdd intrinsics + https://bugs.webkit.org/show_bug.cgi?id=179858 + + Reviewed by Saam Barati. + + * microbenchmarks/map-has-and-set.js: Added. + (test): + * stress/map-set-check-failure.js: Added. + (shouldBe): + (shouldThrow): + (target): + * stress/map-set-cse.js: Added. + (shouldBe): + (test): + * stress/set-add-check-failure.js: Added. + (shouldBe): + (shouldThrow): + (set shouldThrow): + * stress/set-add-cse.js: Added. + (shouldBe): + +2017-11-21 Yusuke Suzuki + [JSC] Allow poly proto for intrinsic getters https://bugs.webkit.org/show_bug.cgi?id=179550 diff --git a/JSTests/microbenchmarks/map-has-and-set.js b/JSTests/microbenchmarks/map-has-and-set.js new file mode 100644 index 0000000..a233940 --- /dev/null +++ b/JSTests/microbenchmarks/map-has-and-set.js @@ -0,0 +1,12 @@ +function test() +{ + let map = new Map(); + for (let i = 0; i < 1e6; ++i) { + if (!map.has(i)) + map.set(i, i); + } + return map +} +noInline(test); + +test(); diff --git a/JSTests/stress/map-set-check-failure.js b/JSTests/stress/map-set-check-failure.js new file mode 100644 index 0000000..6a95327 --- /dev/null +++ b/JSTests/stress/map-set-check-failure.js @@ -0,0 +1,35 @@ +function shouldBe(actual, expected) { + if (actual !== expected) + throw new Error('bad value: ' + actual); +} + +function shouldThrow(func, errorMessage) { + var errorThrown = false; + var error = null; + try { + func(); + } catch (e) { + errorThrown = true; + error = e; + } + if (!errorThrown) + throw new Error('not thrown'); + if (String(error) !== errorMessage) + throw new Error(`bad error: ${String(error)}`); +} + +var func = Map.prototype.set; +function target(map) +{ + return func.call(map, 42, 42); +} +noInline(target); + +for (var i = 0; i < 1e6; ++i) { + var map = new Map(); + shouldBe(target(map), map); + shouldBe(map.get(42), 42); +} +shouldThrow(() => { + target(new Set()); +}, `TypeError: Map operation called on non-Map object`); diff --git a/JSTests/stress/map-set-cse.js b/JSTests/stress/map-set-cse.js new file mode 100644 index 0000000..05495a8 --- /dev/null +++ b/JSTests/stress/map-set-cse.js @@ -0,0 +1,21 @@ +function shouldBe(actual, expected) +{ + if (actual !== expected) + throw new Error('bad value: ' + actual); +} + +function test() +{ + var map = new Map(); + var r1 = map.get(42); + map.set(42, 42); + var r2 = map.get(42); + return [r1, r2]; +} +noInline(test); + +for (var i = 0; i < 1e5; ++i) { + let [r1, r2] = test(); + shouldBe(r1, undefined); + shouldBe(r2, 42); +} diff --git a/JSTests/stress/set-add-check-failure.js b/JSTests/stress/set-add-check-failure.js new file mode 100644 index 0000000..32bd6c1 --- /dev/null +++ b/JSTests/stress/set-add-check-failure.js @@ -0,0 +1,35 @@ +function shouldBe(actual, expected) { + if (actual !== expected) + throw new Error('bad value: ' + actual); +} + +function shouldThrow(func, errorMessage) { + var errorThrown = false; + var error = null; + try { + func(); + } catch (e) { + errorThrown = true; + error = e; + } + if (!errorThrown) + throw new Error('not thrown'); + if (String(error) !== errorMessage) + throw new Error(`bad error: ${String(error)}`); +} + +var func = Set.prototype.add; +function target(set) +{ + return func.call(set, 42); +} +noInline(target); + +for (var i = 0; i < 1e6; ++i) { + var set = new Set(); + shouldBe(target(set), set); + shouldBe(set.has(42), true); +} +shouldThrow(() => { + target(new Map()); +}, `TypeError: Set operation called on non-Set object`); diff --git a/JSTests/stress/set-add-cse.js b/JSTests/stress/set-add-cse.js new file mode 100644 index 0000000..b2252aa --- /dev/null +++ b/JSTests/stress/set-add-cse.js @@ -0,0 +1,21 @@ +function shouldBe(actual, expected) +{ + if (actual !== expected) + throw new Error('bad value: ' + actual); +} + +function test() +{ + var set = new Set(); + var r1 = set.has(42); + set.add(42); + var r2 = set.has(42); + return [r1, r2]; +} +noInline(test); + +for (var i = 0; i < 1e5; ++i) { + let [r1, r2] = test(); + shouldBe(r1, false); + shouldBe(r2, true); +} diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog index db6ac11..ae09767 100644 --- a/Source/JavaScriptCore/ChangeLog +++ b/Source/JavaScriptCore/ChangeLog @@ -1,5 +1,84 @@ 2017-11-21 Yusuke Suzuki + [DFG][FTL] Support MapSet / SetAdd intrinsics + https://bugs.webkit.org/show_bug.cgi?id=179858 + + Reviewed by Saam Barati. + + Map.prototype.set and Set.prototype.add uses MapHash value anyway. + By handling them as MapSet and SetAdd DFG nodes and decoupling + MapSet and SetAdd nodes from MapHash DFG node, we have a chance to + remove duplicate MapHash calculation for the same key. + + One story is *set-if-not-exists*. + + if (!map.has(key)) + map.set(key, value); + + In the above code, both `has` and `set` require hash value for `key`. + If we can change `set` to the series of DFG nodes: + + 1: MapHash(key) + 2: MapSet(MapObjectUse:map, Untyped:key, Untyped:value, Int32Use:@1) + + we can remove duplicate @1 produced by `has` operation. + + This patch improves SixSpeed map-set.es6 and map-set-object.es6 by 20.5% and 20.4% respectively, + + baseline patched + + map-set.es6 246.2413+-15.2084 ^ 204.3679+-11.2408 ^ definitely 1.2049x faster + map-set-object.es6 266.5075+-17.2289 ^ 221.2792+-12.2948 ^ definitely 1.2044x faster + + Microbenchmarks + + map-has-and-set 148.1522+-7.6665 ^ 131.4552+-7.8846 ^ definitely 1.1270x faster + + * dfg/DFGAbstractInterpreterInlines.h: + (JSC::DFG::AbstractInterpreter::executeEffects): + * dfg/DFGByteCodeParser.cpp: + (JSC::DFG::ByteCodeParser::handleIntrinsicCall): + * dfg/DFGClobberize.h: + (JSC::DFG::clobberize): + * dfg/DFGDoesGC.cpp: + (JSC::DFG::doesGC): + * dfg/DFGFixupPhase.cpp: + (JSC::DFG::FixupPhase::fixupNode): + * dfg/DFGNodeType.h: + * dfg/DFGOperations.cpp: + * dfg/DFGOperations.h: + * dfg/DFGPredictionPropagationPhase.cpp: + * dfg/DFGSafeToExecute.h: + (JSC::DFG::safeToExecute): + * dfg/DFGSpeculativeJIT.cpp: + (JSC::DFG::SpeculativeJIT::compileSetAdd): + (JSC::DFG::SpeculativeJIT::compileMapSet): + * dfg/DFGSpeculativeJIT.h: + (JSC::DFG::SpeculativeJIT::callOperation): + * dfg/DFGSpeculativeJIT32_64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * ftl/FTLCapabilities.cpp: + (JSC::FTL::canCompile): + * ftl/FTLLowerDFGToB3.cpp: + (JSC::FTL::DFG::LowerDFGToB3::compileNode): + (JSC::FTL::DFG::LowerDFGToB3::compileSetAdd): + (JSC::FTL::DFG::LowerDFGToB3::compileMapSet): + * jit/JITOperations.h: + * runtime/HashMapImpl.h: + (JSC::HashMapImpl::addNormalized): + (JSC::HashMapImpl::addNormalizedInternal): + * runtime/Intrinsic.cpp: + (JSC::intrinsicName): + * runtime/Intrinsic.h: + * runtime/MapPrototype.cpp: + (JSC::MapPrototype::finishCreation): + * runtime/SetPrototype.cpp: + (JSC::SetPrototype::finishCreation): + +2017-11-21 Yusuke Suzuki + [JSC] Allow poly proto for intrinsic getters https://bugs.webkit.org/show_bug.cgi?id=179550 diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h index 4caaf5f..cb30d02 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h @@ -1141,6 +1141,10 @@ bool AbstractInterpreter::executeEffects(unsigned clobberLimi } break; + case SetAdd: + case MapSet: + break; + case WeakMapGet: forNode(node).makeHeapTop(); break; diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index 7e10a50..b841c7a 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -2904,6 +2904,38 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin return true; } + case JSSetAddIntrinsic: { + if (argumentCountIncludingThis != 2) + return false; + + insertChecks(); + Node* base = get(virtualRegisterForArgument(0, registerOffset)); + Node* key = get(virtualRegisterForArgument(1, registerOffset)); + Node* hash = addToGraph(MapHash, key); + addToGraph(SetAdd, base, key, hash); + set(VirtualRegister(resultOperand), base); + return true; + } + + case JSMapSetIntrinsic: { + if (argumentCountIncludingThis != 3) + return false; + + insertChecks(); + Node* base = get(virtualRegisterForArgument(0, registerOffset)); + Node* key = get(virtualRegisterForArgument(1, registerOffset)); + Node* value = get(virtualRegisterForArgument(2, registerOffset)); + Node* hash = addToGraph(MapHash, key); + + addVarArgChild(base); + addVarArgChild(key); + addVarArgChild(value); + addVarArgChild(hash); + addToGraph(Node::VarArg, MapSet, OpInfo(0), OpInfo(0)); + set(VirtualRegister(resultOperand), base); + return true; + } + case JSSetBucketHeadIntrinsic: case JSMapBucketHeadIntrinsic: { ASSERT(argumentCountIncludingThis == 2); diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h index 1b98c6f..5f37293 100644 --- a/Source/JavaScriptCore/dfg/DFGClobberize.h +++ b/Source/JavaScriptCore/dfg/DFGClobberize.h @@ -1643,6 +1643,14 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu return; } + case SetAdd: + case MapSet: { + // FIXME: Define defs for them to participate in CSE. + // https://bugs.webkit.org/show_bug.cgi?id=179911 + write(MiscFields); + return; + } + case StringSlice: def(PureValue(node)); return; diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp index d2b75e1..fc1ab6c 100644 --- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp +++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp @@ -344,6 +344,8 @@ bool doesGC(Graph& graph, Node* node) case ArraySlice: case ArrayIndexOf: case ParseInt: // We might resolve a rope even though we don't clobber anything. + case SetAdd: + case MapSet: return true; case MultiPutByOffset: diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index e4471bd..b2373fe 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -1947,6 +1947,18 @@ private: break; } + case SetAdd: { + fixEdge(node->child1()); + fixEdge(node->child3()); + break; + } + + case MapSet: { + fixEdge(m_graph.varArgChild(node, 0)); + fixEdge(m_graph.varArgChild(node, 3)); + break; + } + case DefineDataProperty: { fixEdge(m_graph.varArgChild(node, 0)); Edge& propertyEdge = m_graph.varArgChild(node, 1); diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h index fe08c38..6ab5cdc 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeType.h +++ b/Source/JavaScriptCore/dfg/DFGNodeType.h @@ -445,6 +445,8 @@ namespace JSC { namespace DFG { macro(GetMapBucketNext, NodeResultJS) \ macro(LoadKeyFromMapBucket, NodeResultJS) \ macro(LoadValueFromMapBucket, NodeResultJS) \ + macro(SetAdd, NodeMustGenerate) \ + macro(MapSet, NodeMustGenerate | NodeHasVarArgs) \ /* Nodes for JSWeakMap and JSWeakSet */ \ macro(WeakMapGet, NodeResultJS) \ \ diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index a55711d..50802dc 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -2556,6 +2556,22 @@ JSCell* JIT_OPERATION operationJSSetFindBucket(ExecState* exec, JSCell* map, Enc return *bucket; } +// FIXME: Add NormalizeMapKey DFG node. +// https://bugs.webkit.org/show_bug.cgi?id=179912 +void JIT_OPERATION operationSetAdd(ExecState* exec, JSCell* set, EncodedJSValue key, int32_t hash) +{ + VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + jsCast(set)->addNormalized(exec, normalizeMapKey(JSValue::decode(key)), JSValue(), hash); +} + +void JIT_OPERATION operationMapSet(ExecState* exec, JSCell* map, EncodedJSValue key, EncodedJSValue value, int32_t hash) +{ + VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + jsCast(map)->addNormalized(exec, normalizeMapKey(JSValue::decode(key)), JSValue::decode(value), hash); +} + EncodedJSValue JIT_OPERATION operationGetPrototypeOfObject(ExecState* exec, JSObject* thisObject) { VM& vm = exec->vm(); diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h index 5150d22..917fa15 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.h +++ b/Source/JavaScriptCore/dfg/DFGOperations.h @@ -138,6 +138,8 @@ void JIT_OPERATION operationDefineAccessorProperty(ExecState*, JSObject*, Encode void JIT_OPERATION operationDefineAccessorPropertyString(ExecState*, JSObject*, JSString*, JSObject*, JSObject*, int32_t) WTF_INTERNAL; void JIT_OPERATION operationDefineAccessorPropertyStringIdent(ExecState*, JSObject*, UniquedStringImpl*, JSObject*, JSObject*, int32_t) WTF_INTERNAL; void JIT_OPERATION operationDefineAccessorPropertySymbol(ExecState*, JSObject*, Symbol*, JSObject*, JSObject*, int32_t) WTF_INTERNAL; +void JIT_OPERATION operationSetAdd(ExecState*, JSCell*, EncodedJSValue, int32_t) WTF_INTERNAL; +void JIT_OPERATION operationMapSet(ExecState*, JSCell*, EncodedJSValue, EncodedJSValue, int32_t) WTF_INTERNAL; EncodedJSValue JIT_OPERATION operationArrayPush(ExecState*, EncodedJSValue encodedValue, JSArray*) WTF_INTERNAL; EncodedJSValue JIT_OPERATION operationArrayPushMultiple(ExecState*, JSArray*, void* buffer, int32_t elementCount) WTF_INTERNAL; EncodedJSValue JIT_OPERATION operationArrayPushDouble(ExecState*, double value, JSArray*) WTF_INTERNAL; diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index d830578..6d5db77 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -1173,6 +1173,8 @@ private: case PutDynamicVar: case NukeStructureAndSetButterfly: case InitializeEntrypointArguments: + case SetAdd: + case MapSet: break; // This gets ignored because it only pretends to produce a value. diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h index fe708f4..d383b9a 100644 --- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h +++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h @@ -535,6 +535,10 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node) return true; } + case SetAdd: + case MapSet: + return false; + case LastNodeType: RELEASE_ASSERT_NOT_REACHED(); return false; diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 618bac1..e1621d6 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -10836,6 +10836,44 @@ void SpeculativeJIT::compileThrowStaticError(Node* node) noResult(node); } +void SpeculativeJIT::compileSetAdd(Node* node) +{ + SpeculateCellOperand set(this, node->child1()); + JSValueOperand key(this, node->child2()); + SpeculateInt32Operand hash(this, node->child3()); + + GPRReg setGPR = set.gpr(); + JSValueRegs keyRegs = key.jsValueRegs(); + GPRReg hashGPR = hash.gpr(); + + speculateSetObject(node->child1(), setGPR); + + flushRegisters(); + callOperation(operationSetAdd, setGPR, keyRegs, hashGPR); + m_jit.exceptionCheck(); + noResult(node); +} + +void SpeculativeJIT::compileMapSet(Node* node) +{ + SpeculateCellOperand map(this, m_jit.graph().varArgChild(node, 0)); + JSValueOperand key(this, m_jit.graph().varArgChild(node, 1)); + JSValueOperand value(this, m_jit.graph().varArgChild(node, 2)); + SpeculateInt32Operand hash(this, m_jit.graph().varArgChild(node, 3)); + + GPRReg mapGPR = map.gpr(); + JSValueRegs keyRegs = key.jsValueRegs(); + JSValueRegs valueRegs = value.jsValueRegs(); + GPRReg hashGPR = hash.gpr(); + + speculateMapObject(m_jit.graph().varArgChild(node, 0), mapGPR); + + flushRegisters(); + callOperation(operationMapSet, mapGPR, keyRegs, valueRegs, hashGPR); + m_jit.exceptionCheck(); + noResult(node); +} + void SpeculativeJIT::compileWeakMapGet(Node* node) { SpeculateCellOperand weakMap(this, node->child1()); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 99ebf18..2956494 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -1932,11 +1932,21 @@ public: m_jit.setupArgumentsWithExecState(arg1, arg2.payloadGPR()); return appendCall(operation); } + JITCompiler::Call callOperation(V_JITOperation_ECJZ operation, GPRReg arg1, JSValueRegs arg2, GPRReg arg3) + { + m_jit.setupArgumentsWithExecState(arg1, arg2.payloadGPR(), arg3); + return appendCall(operation); + } JITCompiler::Call callOperation(V_JITOperation_ECJJ operation, GPRReg arg1, GPRReg arg2, GPRReg arg3) { m_jit.setupArgumentsWithExecState(arg1, arg2, arg3); return appendCall(operation); } + JITCompiler::Call callOperation(V_JITOperation_ECJJZ operation, GPRReg arg1, JSValueRegs arg2, JSValueRegs arg3, GPRReg arg4) + { + m_jit.setupArgumentsWithExecState(arg1, arg2.payloadGPR(), arg3.payloadGPR(), arg4); + return appendCall(operation); + } JITCompiler::Call callOperation(V_JITOperation_ECCJ operation, GPRReg arg1, GPRReg arg2, JSValueRegs arg3) { m_jit.setupArgumentsWithExecState(arg1, arg2, arg3.payloadGPR()); @@ -2502,6 +2512,16 @@ public: m_jit.setupArgumentsWithExecState(arg1, arg2.payloadGPR(), arg2.tagGPR(), arg3.payloadGPR(), arg3.tagGPR()); return appendCall(operation); } + JITCompiler::Call callOperation(V_JITOperation_ECJZ operation, GPRReg arg1, JSValueRegs arg2, GPRReg arg3) + { + m_jit.setupArgumentsWithExecState(arg1, arg2.payloadGPR(), arg2.tagGPR(), arg3); + return appendCall(operation); + } + JITCompiler::Call callOperation(V_JITOperation_ECJJZ operation, GPRReg arg1, JSValueRegs arg2, JSValueRegs arg3, GPRReg arg4) + { + m_jit.setupArgumentsWithExecState(arg1, arg2.payloadGPR(), arg2.tagGPR(), arg3.payloadGPR(), arg3.tagGPR(), arg4); + return appendCall(operation); + } JITCompiler::Call callOperation(V_JITOperation_ECCJ operation, GPRReg arg1, GPRReg arg2, JSValueRegs arg3) { m_jit.setupArgumentsWithExecState(arg1, arg2, EABI_32BIT_DUMMY_ARG arg3.payloadGPR(), arg3.tagGPR()); @@ -2876,6 +2896,8 @@ public: void compileCheckSubClass(Node*); void compileGetMapBucketHead(Node*); void compileGetMapBucketNext(Node*); + void compileSetAdd(Node*); + void compileMapSet(Node*); void compileWeakMapGet(Node*); void compileLoadKeyFromMapBucket(Node*); void compileLoadValueFromMapBucket(Node*); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index 9dd9297..3e53f96 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -4875,6 +4875,14 @@ void SpeculativeJIT::compile(Node* node) compileLoadValueFromMapBucket(node); break; + case SetAdd: + compileSetAdd(node); + break; + + case MapSet: + compileMapSet(node); + break; + case WeakMapGet: compileWeakMapGet(node); break; diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index ad1a21d..b2f90dd 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -5261,6 +5261,14 @@ void SpeculativeJIT::compile(Node* node) compileLoadValueFromMapBucket(node); break; + case SetAdd: + compileSetAdd(node); + break; + + case MapSet: + compileMapSet(node); + break; + case WeakMapGet: compileWeakMapGet(node); break; diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp index aa55239..e1b467c 100644 --- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp +++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp @@ -207,6 +207,8 @@ inline CapabilityLevel canCompile(Node* node) case GetMapBucketNext: case LoadKeyFromMapBucket: case LoadValueFromMapBucket: + case SetAdd: + case MapSet: case WeakMapGet: case IsEmpty: case IsUndefined: diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp index b461163..f954e87 100644 --- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp +++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp @@ -1052,6 +1052,12 @@ private: case LoadValueFromMapBucket: compileLoadValueFromMapBucket(); break; + case SetAdd: + compileSetAdd(); + break; + case MapSet: + compileMapSet(); + break; case WeakMapGet: compileWeakMapGet(); break; @@ -8786,6 +8792,25 @@ private: setJSValue(m_out.load64(mapBucket, m_heaps.HashMapBucket_key)); } + void compileSetAdd() + { + LValue set = lowSetObject(m_node->child1()); + LValue key = lowJSValue(m_node->child2()); + LValue hash = lowInt32(m_node->child3()); + + vmCall(Void, m_out.operation(operationSetAdd), m_callFrame, set, key, hash); + } + + void compileMapSet() + { + LValue map = lowMapObject(m_graph.varArgChild(m_node, 0)); + LValue key = lowJSValue(m_graph.varArgChild(m_node, 1)); + LValue value = lowJSValue(m_graph.varArgChild(m_node, 2)); + LValue hash = lowInt32(m_graph.varArgChild(m_node, 3)); + + vmCall(Void, m_out.operation(operationMapSet), m_callFrame, map, key, value, hash); + } + void compileWeakMapGet() { LValue weakMap = lowWeakMapObject(m_node->child1()); diff --git a/Source/JavaScriptCore/jit/JITOperations.h b/Source/JavaScriptCore/jit/JITOperations.h index 94714bf..6a5941c 100644 --- a/Source/JavaScriptCore/jit/JITOperations.h +++ b/Source/JavaScriptCore/jit/JITOperations.h @@ -265,7 +265,9 @@ typedef void (JIT_OPERATION *V_JITOperation_ECIZJJ)(ExecState*, JSCell*, Uniqued typedef void (JIT_OPERATION *V_JITOperation_ECJZC)(ExecState*, JSCell*, EncodedJSValue, int32_t, JSCell*); typedef void (JIT_OPERATION *V_JITOperation_ECCIcf)(ExecState*, JSCell*, JSCell*, InlineCallFrame*); typedef void (JIT_OPERATION *V_JITOperation_ECJ)(ExecState*, JSCell*, EncodedJSValue); +typedef void (JIT_OPERATION *V_JITOperation_ECJZ)(ExecState*, JSCell*, EncodedJSValue, int32_t); typedef void (JIT_OPERATION *V_JITOperation_ECJJ)(ExecState*, JSCell*, EncodedJSValue, EncodedJSValue); +typedef void (JIT_OPERATION *V_JITOperation_ECJJZ)(ExecState*, JSCell*, EncodedJSValue, EncodedJSValue, int32_t); typedef void (JIT_OPERATION *V_JITOperation_ECPSPS)(ExecState*, JSCell*, void*, size_t, void*, size_t); typedef void (JIT_OPERATION *V_JITOperation_ECZ)(ExecState*, JSCell*, int32_t); typedef void (JIT_OPERATION *V_JITOperation_ECC)(ExecState*, JSCell*, JSCell*); diff --git a/Source/JavaScriptCore/runtime/HashMapImpl.h b/Source/JavaScriptCore/runtime/HashMapImpl.h index 2887e60..965c4e7 100644 --- a/Source/JavaScriptCore/runtime/HashMapImpl.h +++ b/Source/JavaScriptCore/runtime/HashMapImpl.h @@ -441,6 +441,18 @@ public: rehash(exec); } + ALWAYS_INLINE void addNormalized(ExecState* exec, JSValue key, JSValue value, uint32_t hash) + { + ASSERT_WITH_MESSAGE(normalizeMapKey(key) == key, "We expect normalized values flowing into this function."); + ASSERT_WITH_MESSAGE(jsMapHash(exec, exec->vm(), key) == hash, "We expect hash value is what we expect."); + + addNormalizedInternal(exec->vm(), key, value, hash, [&] (HashMapBucketType* bucket) { + return !isDeleted(bucket) && areKeysEqual(exec, key, bucket->key()); + }); + if (shouldRehashAfterAdd()) + rehash(exec); + } + ALWAYS_INLINE bool remove(ExecState* exec, JSValue key) { HashMapBucketType** bucket = findBucket(exec, key); @@ -556,14 +568,22 @@ private: template ALWAYS_INLINE void addNormalizedInternal(ExecState* exec, JSValue key, JSValue value, const CanUseBucket& canUseBucket) { - ASSERT_WITH_MESSAGE(normalizeMapKey(key) == key, "We expect normalized values flowing into this function."); - VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - const uint32_t mask = m_capacity - 1; - uint32_t index = jsMapHash(exec, vm, key) & mask; + uint32_t hash = jsMapHash(exec, vm, key); RETURN_IF_EXCEPTION(scope, void()); + scope.release(); + addNormalizedInternal(vm, key, value, hash, canUseBucket); + } + + template + ALWAYS_INLINE void addNormalizedInternal(VM& vm, JSValue key, JSValue value, uint32_t hash, const CanUseBucket& canUseBucket) + { + ASSERT_WITH_MESSAGE(normalizeMapKey(key) == key, "We expect normalized values flowing into this function."); + + const uint32_t mask = m_capacity - 1; + uint32_t index = hash & mask; HashMapBucketType** buffer = this->buffer(); HashMapBucketType* bucket = buffer[index]; while (!isEmpty(bucket)) { diff --git a/Source/JavaScriptCore/runtime/Intrinsic.cpp b/Source/JavaScriptCore/runtime/Intrinsic.cpp index 74f8d9e..3931871a 100644 --- a/Source/JavaScriptCore/runtime/Intrinsic.cpp +++ b/Source/JavaScriptCore/runtime/Intrinsic.cpp @@ -143,6 +143,8 @@ const char* intrinsicName(Intrinsic intrinsic) return "JSMapGetIntrinsic"; case JSMapHasIntrinsic: return "JSMapHasIntrinsic"; + case JSMapSetIntrinsic: + return "JSMapSetIntrinsic"; case JSMapBucketHeadIntrinsic: return "JSMapBucketHeadIntrinsic"; case JSMapBucketNextIntrinsic: @@ -153,6 +155,8 @@ const char* intrinsicName(Intrinsic intrinsic) return "JSMapBucketValueIntrinsic"; case JSSetHasIntrinsic: return "JSSetHasIntrinsic"; + case JSSetAddIntrinsic: + return "JSSetAddIntrinsic"; case JSSetBucketHeadIntrinsic: return "JSSetBucketHeadIntrinsic"; case JSSetBucketNextIntrinsic: diff --git a/Source/JavaScriptCore/runtime/Intrinsic.h b/Source/JavaScriptCore/runtime/Intrinsic.h index 470d968..dca7c39 100644 --- a/Source/JavaScriptCore/runtime/Intrinsic.h +++ b/Source/JavaScriptCore/runtime/Intrinsic.h @@ -84,11 +84,13 @@ enum Intrinsic { BoundThisNoArgsFunctionCallIntrinsic, JSMapGetIntrinsic, JSMapHasIntrinsic, + JSMapSetIntrinsic, JSMapBucketHeadIntrinsic, JSMapBucketNextIntrinsic, JSMapBucketKeyIntrinsic, JSMapBucketValueIntrinsic, JSSetHasIntrinsic, + JSSetAddIntrinsic, JSSetBucketHeadIntrinsic, JSSetBucketNextIntrinsic, JSSetBucketKeyIntrinsic, diff --git a/Source/JavaScriptCore/runtime/MapPrototype.cpp b/Source/JavaScriptCore/runtime/MapPrototype.cpp index fddf1b0..ea9a76b 100644 --- a/Source/JavaScriptCore/runtime/MapPrototype.cpp +++ b/Source/JavaScriptCore/runtime/MapPrototype.cpp @@ -66,10 +66,10 @@ void MapPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->deleteKeyword, mapProtoFuncDelete, static_cast(PropertyAttribute::DontEnum), 1); JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->get, mapProtoFuncGet, static_cast(PropertyAttribute::DontEnum), 1, JSMapGetIntrinsic); JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->has, mapProtoFuncHas, static_cast(PropertyAttribute::DontEnum), 1, JSMapHasIntrinsic); - JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->set, mapProtoFuncSet, static_cast(PropertyAttribute::DontEnum), 2); + JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->set, mapProtoFuncSet, static_cast(PropertyAttribute::DontEnum), 2, JSMapSetIntrinsic); JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().getPrivateName(), mapProtoFuncGet, static_cast(PropertyAttribute::DontEnum), 1, JSMapGetIntrinsic); - JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().setPrivateName(), mapProtoFuncSet, static_cast(PropertyAttribute::DontEnum), 2); + JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().setPrivateName(), mapProtoFuncSet, static_cast(PropertyAttribute::DontEnum), 2, JSMapSetIntrinsic); JSFunction* entries = JSFunction::create(vm, mapPrototypeEntriesCodeGenerator(vm), globalObject); putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().entriesPublicName(), entries, static_cast(PropertyAttribute::DontEnum)); diff --git a/Source/JavaScriptCore/runtime/SetPrototype.cpp b/Source/JavaScriptCore/runtime/SetPrototype.cpp index b413901..5db52c2 100644 --- a/Source/JavaScriptCore/runtime/SetPrototype.cpp +++ b/Source/JavaScriptCore/runtime/SetPrototype.cpp @@ -61,12 +61,12 @@ void SetPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) ASSERT(inherits(vm, info())); didBecomePrototype(); - JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->add, setProtoFuncAdd, static_cast(PropertyAttribute::DontEnum), 1); + JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->add, setProtoFuncAdd, static_cast(PropertyAttribute::DontEnum), 1, JSSetAddIntrinsic); JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->clear, setProtoFuncClear, static_cast(PropertyAttribute::DontEnum), 0); JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->deleteKeyword, setProtoFuncDelete, static_cast(PropertyAttribute::DontEnum), 1); JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->has, setProtoFuncHas, static_cast(PropertyAttribute::DontEnum), 1, JSSetHasIntrinsic); JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().hasPrivateName(), setProtoFuncHas, static_cast(PropertyAttribute::DontEnum), 1, JSSetHasIntrinsic); - JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().addPrivateName(), setProtoFuncAdd, static_cast(PropertyAttribute::DontEnum), 1); + JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().addPrivateName(), setProtoFuncAdd, static_cast(PropertyAttribute::DontEnum), 1, JSSetAddIntrinsic); JSFunction* values = JSFunction::create(vm, setPrototypeValuesCodeGenerator(vm), globalObject); putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPublicName(), values, static_cast(PropertyAttribute::DontEnum)); -- 1.8.3.1