[DFG] Optimize WeakMap::get by adding intrinsic and fixup
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Sep 2017 08:19:33 +0000 (08:19 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Sep 2017 08:19:33 +0000 (08:19 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176010

Reviewed by Filip Pizlo.

JSTests:

* microbenchmarks/weak-map-key.js: Added.
(assert):
(objectKey):
(let.start.Date.now):

Source/JavaScriptCore:

It reveals that Ember.js consumes 3.8% of execution time for WeakMap#get.
It is used for meta property for objects (see peekMeta function in Ember.js).

This patch optimizes WeakMap#get.

1. We use inlineGet to inline WeakMap#get operation in the native function.
Since this native function itself is very small, we should inline HashMap#get
entirely in this function.

2. We add JSWeakMapType and JSWeakSetType. This allows us to perform `isJSWeakMap()`
very fast. And this patch wires this to DFG and FTL to add WeakMapObjectUse and WeakSetObjectUse
to drop unnecessary type checking. We add fixup rules for WeakMapGet DFG node by using WeakMapObjectUse,
ObjectUse, and Int32Use.

3. We add intrinsic for WeakMap#get, and handle it in DFG and FTL. We use MapHash to
calculate hash value for the key's Object and use this hash value to look up value from
JSWeakMap's HashMap. Currently, we just call the operationWeakMapGet function in DFG and FTL.
It is worth considering that implementing this operation entirely in JIT, like GetMapBucket.
But anyway, the current one already optimizes the performance, so we leave this for the subsequent
patches.

We currently do not implement any other intrinsics (like, WeakMap#has, WeakSet) because they are
not used in Ember.js right now.

This patch optimizes WeakMap#get by 50%.

                         baseline                  patched

weak-map-key         88.6456+-3.9564     ^     59.1502+-2.2406        ^ definitely 1.4987x faster

* bytecode/DirectEvalCodeCache.h:
(JSC::DirectEvalCodeCache::tryGet):
* bytecode/SpeculatedType.cpp:
(JSC::dumpSpeculation):
(JSC::speculationFromClassInfo):
(JSC::speculationFromJSType):
(JSC::speculationFromString):
* bytecode/SpeculatedType.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::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/DFGHeapLocation.cpp:
(WTF::printInternal):
* dfg/DFGHeapLocation.h:
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::SafeToExecuteEdge::operator()):
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::speculateWeakMapObject):
(JSC::DFG::SpeculativeJIT::speculateWeakSetObject):
(JSC::DFG::SpeculativeJIT::speculate):
(JSC::DFG::SpeculativeJIT::compileWeakMapGet):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGUseKind.cpp:
(WTF::printInternal):
* dfg/DFGUseKind.h:
(JSC::DFG::typeFilterFor):
(JSC::DFG::isCell):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileWeakMapGet):
(JSC::FTL::DFG::LowerDFGToB3::lowWeakMapObject):
(JSC::FTL::DFG::LowerDFGToB3::lowWeakSetObject):
(JSC::FTL::DFG::LowerDFGToB3::speculate):
(JSC::FTL::DFG::LowerDFGToB3::speculateWeakMapObject):
(JSC::FTL::DFG::LowerDFGToB3::speculateWeakSetObject):
* jit/JITOperations.h:
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSType.h:
* runtime/JSWeakMap.h:
(JSC::isJSWeakMap):
* runtime/JSWeakSet.h:
(JSC::isJSWeakSet):
* runtime/WeakMapBase.cpp:
(JSC::WeakMapBase::get):
* runtime/WeakMapBase.h:
(JSC::WeakMapBase::HashTranslator::hash):
(JSC::WeakMapBase::HashTranslator::equal):
(JSC::WeakMapBase::inlineGet):
* runtime/WeakMapPrototype.cpp:
(JSC::WeakMapPrototype::finishCreation):
(JSC::getWeakMap):
(JSC::protoFuncWeakMapGet):
* runtime/WeakSetPrototype.cpp:
(JSC::getWeakSet):

Source/WebCore:

* platform/network/curl/CurlJobManager.cpp:
(WebCore::CurlJobList::finishJobs):

Source/WTF:

Add inlineGet method with HashTranslator.

* wtf/HashMap.h:
(WTF::X>::inlineGet const):
(WTF::MappedTraits>::inlineGet const):
(WTF::MappedTraits>::fastGet const): Deleted.
* wtf/LoggingHashMap.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@221854 268f45cc-cd09-0410-ab3c-d52691b4dbfc

42 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/weak-map-key.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/DirectEvalCodeCache.h
Source/JavaScriptCore/bytecode/SpeculatedType.cpp
Source/JavaScriptCore/bytecode/SpeculatedType.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
Source/JavaScriptCore/dfg/DFGHeapLocation.h
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGUseKind.cpp
Source/JavaScriptCore/dfg/DFGUseKind.h
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/runtime/Intrinsic.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSType.h
Source/JavaScriptCore/runtime/JSWeakMap.h
Source/JavaScriptCore/runtime/JSWeakSet.h
Source/JavaScriptCore/runtime/WeakMapBase.cpp
Source/JavaScriptCore/runtime/WeakMapBase.h
Source/JavaScriptCore/runtime/WeakMapPrototype.cpp
Source/JavaScriptCore/runtime/WeakSetPrototype.cpp
Source/WTF/ChangeLog
Source/WTF/wtf/HashMap.h
Source/WTF/wtf/LoggingHashMap.h
Source/WebCore/ChangeLog
Source/WebCore/platform/network/curl/CurlJobManager.cpp

index 71c1746..fecb4ee 100644 (file)
@@ -1,3 +1,15 @@
+2017-09-03  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DFG] Optimize WeakMap::get by adding intrinsic and fixup
+        https://bugs.webkit.org/show_bug.cgi?id=176010
+
+        Reviewed by Filip Pizlo.
+
+        * microbenchmarks/weak-map-key.js: Added.
+        (assert):
+        (objectKey):
+        (let.start.Date.now):
+
 2017-09-09  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Optimize Object.keys by using careful array allocation
diff --git a/JSTests/microbenchmarks/weak-map-key.js b/JSTests/microbenchmarks/weak-map-key.js
new file mode 100644 (file)
index 0000000..7f53ceb
--- /dev/null
@@ -0,0 +1,34 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad!");
+}
+noInline(assert);
+
+let weakMap = new WeakMap;
+
+function objectKey(o) {
+    return weakMap.get(o);
+}
+noInline(objectKey);
+
+const iters = 300000;
+let start = Date.now();
+
+{
+    let o = {f: 20};
+    var array = [];
+    for (var i = 0; i < 10; i++) {
+        let newObject = { f: i };
+        weakMap.set(newObject, i);
+        array[i] = newObject;
+    }
+
+    for (var j = 0; j < iters; ++j) {
+        for (let i = 0; i < 10; i++)
+            assert(objectKey(array[i]) === i);
+    }
+}
+
+const verbose = false;
+if (verbose)
+    print(Date.now() - start);
index 3970f9b..abcceac 100644 (file)
@@ -1,3 +1,118 @@
+2017-09-03  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DFG] Optimize WeakMap::get by adding intrinsic and fixup
+        https://bugs.webkit.org/show_bug.cgi?id=176010
+
+        Reviewed by Filip Pizlo.
+
+        It reveals that Ember.js consumes 3.8% of execution time for WeakMap#get.
+        It is used for meta property for objects (see peekMeta function in Ember.js).
+
+        This patch optimizes WeakMap#get.
+
+        1. We use inlineGet to inline WeakMap#get operation in the native function.
+        Since this native function itself is very small, we should inline HashMap#get
+        entirely in this function.
+
+        2. We add JSWeakMapType and JSWeakSetType. This allows us to perform `isJSWeakMap()`
+        very fast. And this patch wires this to DFG and FTL to add WeakMapObjectUse and WeakSetObjectUse
+        to drop unnecessary type checking. We add fixup rules for WeakMapGet DFG node by using WeakMapObjectUse,
+        ObjectUse, and Int32Use.
+
+        3. We add intrinsic for WeakMap#get, and handle it in DFG and FTL. We use MapHash to
+        calculate hash value for the key's Object and use this hash value to look up value from
+        JSWeakMap's HashMap. Currently, we just call the operationWeakMapGet function in DFG and FTL.
+        It is worth considering that implementing this operation entirely in JIT, like GetMapBucket.
+        But anyway, the current one already optimizes the performance, so we leave this for the subsequent
+        patches.
+
+        We currently do not implement any other intrinsics (like, WeakMap#has, WeakSet) because they are
+        not used in Ember.js right now.
+
+        This patch optimizes WeakMap#get by 50%.
+
+                                 baseline                  patched
+
+        weak-map-key         88.6456+-3.9564     ^     59.1502+-2.2406        ^ definitely 1.4987x faster
+
+        * bytecode/DirectEvalCodeCache.h:
+        (JSC::DirectEvalCodeCache::tryGet):
+        * bytecode/SpeculatedType.cpp:
+        (JSC::dumpSpeculation):
+        (JSC::speculationFromClassInfo):
+        (JSC::speculationFromJSType):
+        (JSC::speculationFromString):
+        * bytecode/SpeculatedType.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::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/DFGHeapLocation.cpp:
+        (WTF::printInternal):
+        * dfg/DFGHeapLocation.h:
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::SafeToExecuteEdge::operator()):
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::speculateWeakMapObject):
+        (JSC::DFG::SpeculativeJIT::speculateWeakSetObject):
+        (JSC::DFG::SpeculativeJIT::speculate):
+        (JSC::DFG::SpeculativeJIT::compileWeakMapGet):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGUseKind.cpp:
+        (WTF::printInternal):
+        * dfg/DFGUseKind.h:
+        (JSC::DFG::typeFilterFor):
+        (JSC::DFG::isCell):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileWeakMapGet):
+        (JSC::FTL::DFG::LowerDFGToB3::lowWeakMapObject):
+        (JSC::FTL::DFG::LowerDFGToB3::lowWeakSetObject):
+        (JSC::FTL::DFG::LowerDFGToB3::speculate):
+        (JSC::FTL::DFG::LowerDFGToB3::speculateWeakMapObject):
+        (JSC::FTL::DFG::LowerDFGToB3::speculateWeakSetObject):
+        * jit/JITOperations.h:
+        * runtime/Intrinsic.cpp:
+        (JSC::intrinsicName):
+        * runtime/Intrinsic.h:
+        * runtime/JSType.h:
+        * runtime/JSWeakMap.h:
+        (JSC::isJSWeakMap):
+        * runtime/JSWeakSet.h:
+        (JSC::isJSWeakSet):
+        * runtime/WeakMapBase.cpp:
+        (JSC::WeakMapBase::get):
+        * runtime/WeakMapBase.h:
+        (JSC::WeakMapBase::HashTranslator::hash):
+        (JSC::WeakMapBase::HashTranslator::equal):
+        (JSC::WeakMapBase::inlineGet):
+        * runtime/WeakMapPrototype.cpp:
+        (JSC::WeakMapPrototype::finishCreation):
+        (JSC::getWeakMap):
+        (JSC::protoFuncWeakMapGet):
+        * runtime/WeakSetPrototype.cpp:
+        (JSC::getWeakSet):
+
 2017-09-09  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Optimize Object.keys by using careful array allocation
index e075357..596f268 100644 (file)
@@ -86,7 +86,7 @@ namespace JSC {
 
         DirectEvalExecutable* tryGet(const String& evalSource, CallSiteIndex callSiteIndex)
         {
-            return m_cacheMap.fastGet(CacheKey(evalSource, callSiteIndex)).get();
+            return m_cacheMap.inlineGet(CacheKey(evalSource, callSiteIndex)).get();
         }
         
         void set(ExecState* exec, JSCell* owner, const String& evalSource, CallSiteIndex callSiteIndex, DirectEvalExecutable* evalExecutable)
index ca25947..be9e680 100644 (file)
@@ -35,6 +35,8 @@
 #include "JSFunction.h"
 #include "JSMap.h"
 #include "JSSet.h"
+#include "JSWeakMap.h"
+#include "JSWeakSet.h"
 #include "ProxyObject.h"
 #include "RegExpObject.h"
 #include "ScopedArguments.h"
@@ -180,6 +182,16 @@ void dumpSpeculation(PrintStream& outStream, SpeculatedType value)
             else
                 isTop = false;
 
+            if (value & SpecWeakMapObject)
+                strOut.print("WeakMapObject");
+            else
+                isTop = false;
+
+            if (value & SpecWeakSetObject)
+                strOut.print("WeakSetObject");
+            else
+                isTop = false;
+
             if (value & SpecProxyObject)
                 strOut.print("ProxyObject");
             else
@@ -402,6 +414,12 @@ SpeculatedType speculationFromClassInfo(const ClassInfo* classInfo)
     if (classInfo == JSSet::info())
         return SpecSetObject;
 
+    if (classInfo == JSWeakMap::info())
+        return SpecWeakMapObject;
+
+    if (classInfo == JSWeakSet::info())
+        return SpecWeakSetObject;
+
     if (classInfo == ProxyObject::info())
         return SpecProxyObject;
     
@@ -520,6 +538,10 @@ SpeculatedType speculationFromJSType(JSType type)
         return SpecMapObject;
     case JSSetType:
         return SpecSetObject;
+    case JSWeakMapType:
+        return SpecWeakMapObject;
+    case JSWeakSetType:
+        return SpecWeakSetObject;
     default:
         ASSERT_NOT_REACHED();
     }
@@ -701,6 +723,10 @@ SpeculatedType speculationFromString(const char* speculation)
         return SpecMapObject;
     if (!strncmp(speculation, "SpecSetObject", strlen("SpecSetObject")))
         return SpecSetObject;
+    if (!strncmp(speculation, "SpecWeakMapObject", strlen("SpecWeakMapObject")))
+        return SpecWeakMapObject;
+    if (!strncmp(speculation, "SpecWeakSetObject", strlen("SpecWeakSetObject")))
+        return SpecWeakSetObject;
     if (!strncmp(speculation, "SpecProxyObject", strlen("SpecProxyObject")))
         return SpecProxyObject;
     if (!strncmp(speculation, "SpecDerivedArray", strlen("SpecDerivedArray")))
index bab60cc..2db85da 100644 (file)
@@ -58,26 +58,28 @@ static const SpeculatedType SpecStringObject       = 1ull << 14; // It's definit
 static const SpeculatedType SpecRegExpObject       = 1ull << 15; // It's definitely a RegExpObject (and not any subclass of RegExpObject).
 static const SpeculatedType SpecMapObject          = 1ull << 16; // It's definitely a Map object or one of its subclasses.
 static const SpeculatedType SpecSetObject          = 1ull << 17; // It's definitely a Set object or one of its subclasses.
-static const SpeculatedType SpecProxyObject        = 1ull << 18; // It's definitely a Proxy object or one of its subclasses.
-static const SpeculatedType SpecDerivedArray       = 1ull << 19; // It's definitely a DerivedArray object.
-static const SpeculatedType SpecObjectOther        = 1ull << 20; // It's definitely an object but not JSFinalObject, JSArray, or JSFunction.
-static const SpeculatedType SpecObject             = SpecFinalObject | SpecArray | SpecFunction | SpecTypedArrayView | SpecDirectArguments | SpecScopedArguments | SpecStringObject | SpecRegExpObject | SpecMapObject | SpecSetObject | SpecProxyObject | SpecDerivedArray | SpecObjectOther; // Bitmask used for testing for any kind of object prediction.
-static const SpeculatedType SpecStringIdent        = 1ull << 21; // It's definitely a JSString, and it's an identifier.
-static const SpeculatedType SpecStringVar          = 1ull << 22; // It's definitely a JSString, and it's not an identifier.
+static const SpeculatedType SpecWeakMapObject      = 1ull << 18; // It's definitely a WeakMap object or one of its subclasses.
+static const SpeculatedType SpecWeakSetObject      = 1ull << 19; // It's definitely a WeakSet object or one of its subclasses.
+static const SpeculatedType SpecProxyObject        = 1ull << 20; // It's definitely a Proxy object or one of its subclasses.
+static const SpeculatedType SpecDerivedArray       = 1ull << 21; // It's definitely a DerivedArray object.
+static const SpeculatedType SpecObjectOther        = 1ull << 22; // It's definitely an object but not JSFinalObject, JSArray, or JSFunction.
+static const SpeculatedType SpecObject             = SpecFinalObject | SpecArray | SpecFunction | SpecTypedArrayView | SpecDirectArguments | SpecScopedArguments | SpecStringObject | SpecRegExpObject | SpecMapObject | SpecSetObject | SpecWeakMapObject | SpecWeakSetObject | SpecProxyObject | SpecDerivedArray | SpecObjectOther; // Bitmask used for testing for any kind of object prediction.
+static const SpeculatedType SpecStringIdent        = 1ull << 23; // It's definitely a JSString, and it's an identifier.
+static const SpeculatedType SpecStringVar          = 1ull << 24; // It's definitely a JSString, and it's not an identifier.
 static const SpeculatedType SpecString             = SpecStringIdent | SpecStringVar; // It's definitely a JSString.
-static const SpeculatedType SpecSymbol             = 1ull << 23; // It's definitely a Symbol.
-static const SpeculatedType SpecCellOther          = 1ull << 24; // It's definitely a JSCell but not a subclass of JSObject and definitely not a JSString or a Symbol.
+static const SpeculatedType SpecSymbol             = 1ull << 25; // It's definitely a Symbol.
+static const SpeculatedType SpecCellOther          = 1ull << 26; // It's definitely a JSCell but not a subclass of JSObject and definitely not a JSString or a Symbol.
 static const SpeculatedType SpecCell               = SpecObject | SpecString | SpecSymbol | SpecCellOther; // It's definitely a JSCell.
-static const SpeculatedType SpecBoolInt32          = 1ull << 25; // It's definitely an Int32 with value 0 or 1.
-static const SpeculatedType SpecNonBoolInt32       = 1ull << 26; // It's definitely an Int32 with value other than 0 or 1.
+static const SpeculatedType SpecBoolInt32          = 1ull << 27; // It's definitely an Int32 with value 0 or 1.
+static const SpeculatedType SpecNonBoolInt32       = 1ull << 28; // It's definitely an Int32 with value other than 0 or 1.
 static const SpeculatedType SpecInt32Only          = SpecBoolInt32 | SpecNonBoolInt32; // It's definitely an Int32.
-static const SpeculatedType SpecInt52Only          = 1ull << 27; // It's definitely an Int52 and we intend it to unbox it. It's also definitely not an Int32.
+static const SpeculatedType SpecInt52Only          = 1ull << 29; // It's definitely an Int52 and we intend it to unbox it. It's also definitely not an Int32.
 static const SpeculatedType SpecAnyInt             = SpecInt32Only | SpecInt52Only; // It's something that we can do machine int arithmetic on.
-static const SpeculatedType SpecAnyIntAsDouble     = 1ull << 28; // It's definitely an Int52 and it's inside a double.
-static const SpeculatedType SpecNonIntAsDouble     = 1ull << 29; // It's definitely not an Int52 but it's a real number and it's a double.
+static const SpeculatedType SpecAnyIntAsDouble     = 1ull << 30; // It's definitely an Int52 and it's inside a double.
+static const SpeculatedType SpecNonIntAsDouble     = 1ull << 31; // It's definitely not an Int52 but it's a real number and it's a double.
 static const SpeculatedType SpecDoubleReal         = SpecNonIntAsDouble | SpecAnyIntAsDouble; // It's definitely a non-NaN double.
-static const SpeculatedType SpecDoublePureNaN      = 1ull << 30; // It's definitely a NaN that is safe to tag (i.e. pure).
-static const SpeculatedType SpecDoubleImpureNaN    = 1ull << 31; // It's definitely a NaN that is unsafe to tag (i.e. impure).
+static const SpeculatedType SpecDoublePureNaN      = 1ull << 32; // It's definitely a NaN that is safe to tag (i.e. pure).
+static const SpeculatedType SpecDoubleImpureNaN    = 1ull << 33; // It's definitely a NaN that is unsafe to tag (i.e. impure).
 static const SpeculatedType SpecDoubleNaN          = SpecDoublePureNaN | SpecDoubleImpureNaN; // It's definitely some kind of NaN.
 static const SpeculatedType SpecBytecodeDouble     = SpecDoubleReal | SpecDoublePureNaN; // It's either a non-NaN or a NaN double, but it's definitely not impure NaN.
 static const SpeculatedType SpecFullDouble         = SpecDoubleReal | SpecDoubleNaN; // It's either a non-NaN or a NaN double.
@@ -85,12 +87,12 @@ static const SpeculatedType SpecBytecodeRealNumber = SpecInt32Only | SpecDoubleR
 static const SpeculatedType SpecFullRealNumber     = SpecAnyInt | SpecDoubleReal; // It's either an Int32 or a DoubleReal, or a Int52.
 static const SpeculatedType SpecBytecodeNumber     = SpecInt32Only | SpecBytecodeDouble; // It's either an Int32 or a Double, and the Double cannot be an impure NaN.
 static const SpeculatedType SpecFullNumber         = SpecAnyInt | SpecFullDouble; // It's either an Int32, Int52, or a Double, and the Double can be impure NaN.
-static const SpeculatedType SpecBoolean            = 1ull << 32; // It's definitely a Boolean.
-static const SpeculatedType SpecOther              = 1ull << 33; // It's definitely either Null or Undefined.
+static const SpeculatedType SpecBoolean            = 1ull << 34; // It's definitely a Boolean.
+static const SpeculatedType SpecOther              = 1ull << 35; // It's definitely either Null or Undefined.
 static const SpeculatedType SpecMisc               = SpecBoolean | SpecOther; // It's definitely either a boolean, Null, or Undefined.
 static const SpeculatedType SpecHeapTop            = SpecCell | SpecBytecodeNumber | SpecMisc; // It can be any of the above, except for SpecInt52Only and SpecDoubleImpureNaN.
 static const SpeculatedType SpecPrimitive          = SpecString | SpecSymbol | SpecBytecodeNumber | SpecMisc; // It's any non-Object JSValue.
-static const SpeculatedType SpecEmpty              = 1ull << 34; // It's definitely an empty value marker.
+static const SpeculatedType SpecEmpty              = 1ull << 36; // It's definitely an empty value marker.
 static const SpeculatedType SpecBytecodeTop        = SpecHeapTop | SpecEmpty; // It can be any of the above, except for SpecInt52Only and SpecDoubleImpureNaN. Corresponds to what could be found in a bytecode local.
 static const SpeculatedType SpecFullTop            = SpecBytecodeTop | SpecFullNumber; // It can be anything that bytecode could see plus exotic encodings of numbers.
 
index 3014854..0c7224a 100644 (file)
@@ -1099,6 +1099,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         }
         break;
 
+    case WeakMapGet:
+        forNode(node).makeHeapTop();
+        break;
+
     case IsEmpty:
     case IsUndefined:
     case IsBoolean:
index b49f2e9..1b5d7ae 100644 (file)
@@ -2975,6 +2975,22 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         return true;
     }
 
+    case JSWeakMapGetIntrinsic: {
+        if (argumentCountIncludingThis != 2)
+            return false;
+
+        if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType))
+            return false;
+
+        insertChecks();
+        Node* map = get(virtualRegisterForArgument(0, registerOffset));
+        Node* key = get(virtualRegisterForArgument(1, registerOffset));
+        Node* hash = addToGraph(MapHash, key);
+        Node* result = addToGraph(WeakMapGet, OpInfo(), OpInfo(prediction), map, key, hash);
+        set(VirtualRegister(resultOperand), result);
+        return true;
+    }
+
     case HasOwnPropertyIntrinsic: {
         if (argumentCountIncludingThis != 2)
             return false;
index 52bd8c1..711a453 100644 (file)
@@ -1547,6 +1547,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case MapHash:
         def(PureValue(node));
         return;
+
     case GetMapBucket: {
         read(MiscFields);
         Edge& mapEdge = node->child1();
@@ -1554,12 +1555,14 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         def(HeapLocation(MapBucketLoc, MiscFields, mapEdge, keyEdge), LazyNode(node));
         return;
     }
+
     case GetMapBucketHead: {
         read(MiscFields);
         Edge& mapEdge = node->child1();
         def(HeapLocation(MapBucketHeadLoc, MiscFields, mapEdge), LazyNode(node));
         return;
     }
+
     case GetMapBucketNext: {
         read(MiscFields);
         LocationKind locationKind = MapBucketMapNextLoc;
@@ -1569,12 +1572,14 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         def(HeapLocation(locationKind, MiscFields, bucketEdge), LazyNode(node));
         return;
     }
+
     case LoadKeyFromMapBucket: {
         read(MiscFields);
         Edge& bucketEdge = node->child1();
         def(HeapLocation(MapBucketKeyLoc, MiscFields, bucketEdge), LazyNode(node));
         return;
     }
+
     case LoadValueFromMapBucket: {
         read(MiscFields);
         Edge& bucketEdge = node->child1();
@@ -1582,6 +1587,14 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         return;
     }
 
+    case WeakMapGet: {
+        read(MiscFields);
+        Edge& mapEdge = node->child1();
+        Edge& keyEdge = node->child2();
+        def(HeapLocation(WeakMapGetLoc, MiscFields, mapEdge, keyEdge), LazyNode(node));
+        return;
+    }
+
     case ToLowerCase:
         def(PureValue(node));
         return;
index 083c755..da71976 100644 (file)
@@ -203,6 +203,7 @@ bool doesGC(Graph& graph, Node* node)
     case GetMapBucketNext:
     case LoadKeyFromMapBucket:
     case LoadValueFromMapBucket:
+    case WeakMapGet:
     case Unreachable:
     case ExtractCatchLocal:
     case ExtractOSREntryLocal:
index 2ff6bf0..629894b 100644 (file)
@@ -1900,6 +1900,13 @@ private:
             break;
         }
 
+        case WeakMapGet: {
+            fixEdge<WeakMapObjectUse>(node->child1());
+            fixEdge<ObjectUse>(node->child2());
+            fixEdge<Int32Use>(node->child3());
+            break;
+        }
+
         case DefineDataProperty: {
             fixEdge<CellUse>(m_graph.varArgChild(node, 0));
             Edge& propertyEdge = m_graph.varArgChild(node, 1);
index 60bd572..4ed0e47 100644 (file)
@@ -159,24 +159,35 @@ void printInternal(PrintStream& out, LocationKind kind)
     case RegExpObjectLastIndexLoc:
         out.print("RegExpObjectLastIndexLoc");
         return;
+
     case MapBucketLoc:
         out.print("MapBucketLoc");
         return;
+
     case MapBucketHeadLoc:
         out.print("MapBucketHeadLoc");
         return;
+
     case MapBucketKeyLoc:
         out.print("MapBucketKeyLoc");
         return;
+
     case MapBucketValueLoc:
         out.print("MapBucketValueLoc");
         return;
+
     case MapBucketMapNextLoc:
         out.print("MapBucketMapNextLoc");
         return;
+
     case MapBucketSetNextLoc:
         out.print("MapBucketSetNextLoc");
         return;
+
+    case WeakMapGetLoc:
+        out.print("WeakMapGetLoc");
+        return;
+
     case DOMStateLoc:
         out.print("DOMStateLoc");
         return;
index b82c922..676565c 100644 (file)
@@ -68,6 +68,7 @@ enum LocationKind {
     MapBucketKeyLoc,
     MapBucketMapNextLoc,
     MapBucketSetNextLoc,
+    WeakMapGetLoc,
     DOMStateLoc,
 };
 
index 2139190..bfc9599 100644 (file)
@@ -1579,6 +1579,7 @@ public:
         case AtomicsSub:
         case AtomicsXor:
         case GetDynamicVar:
+        case WeakMapGet:
             return true;
         default:
             return false;
index 0e5bb78..138f236 100644 (file)
@@ -437,6 +437,8 @@ namespace JSC { namespace DFG {
     macro(GetMapBucketNext, NodeResultJS) \
     macro(LoadKeyFromMapBucket, NodeResultJS) \
     macro(LoadValueFromMapBucket, NodeResultJS) \
+    /* Nodes for JSWeakMap and JSWeakSet */ \
+    macro(WeakMapGet, NodeResultJS) \
     \
     macro(ToLowerCase, NodeResultJS) \
     /* Nodes for DOM JIT */\
index 03b7333..b383d2a 100644 (file)
@@ -55,6 +55,7 @@
 #include "JSLexicalEnvironment.h"
 #include "JSMap.h"
 #include "JSSet.h"
+#include "JSWeakMap.h"
 #include "ObjectConstructor.h"
 #include "Operations.h"
 #include "ParseInt.h"
@@ -944,6 +945,13 @@ EncodedJSValue JIT_OPERATION operationRegExpExecGeneric(ExecState* exec, JSGloba
     return JSValue::encode(asRegExpObject(base)->exec(exec, globalObject, input));
 }
 
+EncodedJSValue JIT_OPERATION operationWeakMapGet(ExecState* exec, JSCell* weakMap, JSCell* object, int32_t hash)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    return JSValue::encode(jsCast<JSWeakMap*>(weakMap)->inlineGet(asObject(object), hash));
+}
+
 EncodedJSValue JIT_OPERATION operationParseIntNoRadixGeneric(ExecState* exec, EncodedJSValue value)
 {
     VM& vm = exec->vm();
index 42ca784..5476bb1 100644 (file)
@@ -141,6 +141,7 @@ EncodedJSValue JIT_OPERATION operationArrayPopAndRecoverLength(ExecState*, JSArr
 EncodedJSValue JIT_OPERATION operationRegExpExecString(ExecState*, JSGlobalObject*, RegExpObject*, JSString*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationRegExpExec(ExecState*, JSGlobalObject*, RegExpObject*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationRegExpExecGeneric(ExecState*, JSGlobalObject*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationWeakMapGet(ExecState*, JSCell*, JSCell*, int32_t) WTF_INTERNAL;
 // These comparisons return a boolean within a size_t such that the value is zero extended to fill the register.
 size_t JIT_OPERATION operationRegExpTestString(ExecState*, JSGlobalObject*, RegExpObject*, JSString*) WTF_INTERNAL;
 size_t JIT_OPERATION operationRegExpTest(ExecState*, JSGlobalObject*, RegExpObject*, EncodedJSValue) WTF_INTERNAL;
index 954f794..f6f5f57 100644 (file)
@@ -722,7 +722,8 @@ private:
         case ToNumber:
         case GetArgument:
         case CallDOMGetter:
-        case GetDynamicVar: {
+        case GetDynamicVar:
+        case WeakMapGet: {
             setPrediction(m_currentNode->getHeapPrediction());
             break;
         }
index 3cbd950..2b0c38f 100644 (file)
@@ -63,6 +63,8 @@ public:
         case DerivedArrayUse:
         case MapObjectUse:
         case SetObjectUse:
+        case WeakMapObjectUse:
+        case WeakSetObjectUse:
         case ObjectOrOtherUse:
         case StringIdentUse:
         case StringUse:
@@ -412,6 +414,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case GetMapBucketNext:
     case LoadKeyFromMapBucket:
     case LoadValueFromMapBucket:
+    case WeakMapGet:
     case AtomicsAdd:
     case AtomicsAnd:
     case AtomicsCompareExchange:
index a7fc44e..9291e51 100644 (file)
@@ -8984,6 +8984,34 @@ void SpeculativeJIT::speculateSetObject(Edge edge)
     speculateSetObject(edge, operand.gpr());
 }
 
+void SpeculativeJIT::speculateWeakMapObject(Edge edge, GPRReg cell)
+{
+    speculateCellType(edge, cell, SpecWeakMapObject, JSWeakMapType);
+}
+
+void SpeculativeJIT::speculateWeakMapObject(Edge edge)
+{
+    if (!needsTypeCheck(edge, SpecWeakMapObject))
+        return;
+
+    SpeculateCellOperand operand(this, edge);
+    speculateWeakMapObject(edge, operand.gpr());
+}
+
+void SpeculativeJIT::speculateWeakSetObject(Edge edge, GPRReg cell)
+{
+    speculateCellType(edge, cell, SpecWeakSetObject, JSWeakSetType);
+}
+
+void SpeculativeJIT::speculateWeakSetObject(Edge edge)
+{
+    if (!needsTypeCheck(edge, SpecWeakSetObject))
+        return;
+
+    SpeculateCellOperand operand(this, edge);
+    speculateWeakSetObject(edge, operand.gpr());
+}
+
 void SpeculativeJIT::speculateObjectOrOther(Edge edge)
 {
     if (!needsTypeCheck(edge, SpecObject | SpecOther))
@@ -9302,6 +9330,12 @@ void SpeculativeJIT::speculate(Node*, Edge edge)
     case SetObjectUse:
         speculateSetObject(edge);
         break;
+    case WeakMapObjectUse:
+        speculateWeakMapObject(edge);
+        break;
+    case WeakSetObjectUse:
+        speculateWeakSetObject(edge);
+        break;
     case ObjectOrOtherUse:
         speculateObjectOrOther(edge);
         break;
@@ -10407,6 +10441,28 @@ void SpeculativeJIT::compileThrowStaticError(Node* node)
     noResult(node);
 }
 
+void SpeculativeJIT::compileWeakMapGet(Node* node)
+{
+    SpeculateCellOperand weakMap(this, node->child1());
+    SpeculateCellOperand object(this, node->child2());
+    SpeculateInt32Operand hash(this, node->child3());
+    JSValueRegsTemporary result(this);
+
+    GPRReg weakMapGPR = weakMap.gpr();
+    GPRReg objectGPR = object.gpr();
+    GPRReg hashGPR = hash.gpr();
+    JSValueRegs resultRegs = result.regs();
+
+    speculateWeakMapObject(node->child1(), weakMapGPR);
+    speculateObject(node->child2(), objectGPR);
+
+    flushRegisters();
+    callOperation(operationWeakMapGet, resultRegs, weakMapGPR, objectGPR, hashGPR);
+    m_jit.exceptionCheck();
+
+    jsValueResult(resultRegs, node);
+}
+
 } } // namespace JSC::DFG
 
 #endif
index ae9b24c..bb55ce3 100644 (file)
@@ -1828,6 +1828,11 @@ public:
         m_jit.setupArgumentsWithExecState(arg1, arg2);
         return appendCallSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(J_JITOperation_ECCZ operation, JSValueRegs result, GPRReg arg1, GPRReg arg2, GPRReg arg3)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
+        return appendCallSetResult(operation, result.payloadGPR());
+    }
     JITCompiler::Call callOperation(J_JITOperation_ECJ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
     {
         m_jit.setupArgumentsWithExecState(arg1, arg2);
@@ -2401,6 +2406,11 @@ public:
         m_jit.setupArgumentsWithExecState(arg1, arg2);
         return appendCallSetResult(operation, result.payloadGPR(), result.tagGPR());
     }
+    JITCompiler::Call callOperation(J_JITOperation_ECCZ operation, JSValueRegs result, GPRReg arg1, GPRReg arg2, GPRReg arg3)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
+        return appendCallSetResult(operation, result.payloadGPR(), result.tagGPR());
+    }
 
     JITCompiler::Call callOperation(V_JITOperation_EOZD operation, GPRReg arg1, GPRReg arg2, FPRReg arg3)
     {
@@ -2801,6 +2811,7 @@ public:
     void compileCheckSubClass(Node*);
     void compileGetMapBucketHead(Node*);
     void compileGetMapBucketNext(Node*);
+    void compileWeakMapGet(Node*);
     void compileLoadKeyFromMapBucket(Node*);
     void compileLoadValueFromMapBucket(Node*);
     
@@ -3062,6 +3073,10 @@ public:
     void speculateMapObject(Edge, GPRReg cell);
     void speculateSetObject(Edge);
     void speculateSetObject(Edge, GPRReg cell);
+    void speculateWeakMapObject(Edge);
+    void speculateWeakMapObject(Edge, GPRReg cell);
+    void speculateWeakSetObject(Edge);
+    void speculateWeakSetObject(Edge, GPRReg cell);
     void speculateObjectOrOther(Edge);
     void speculateString(Edge edge, GPRReg cell);
     void speculateStringIdentAndLoadStorage(Edge edge, GPRReg string, GPRReg storage);
index afc4abc..e4a8d17 100644 (file)
@@ -4964,6 +4964,10 @@ void SpeculativeJIT::compile(Node* node)
         compileLoadValueFromMapBucket(node);
         break;
 
+    case WeakMapGet:
+        compileWeakMapGet(node);
+        break;
+
     case Flush:
         break;
 
index 2e65d9f..1cdf5f9 100644 (file)
@@ -5334,6 +5334,10 @@ void SpeculativeJIT::compile(Node* node)
         compileLoadValueFromMapBucket(node);
         break;
 
+    case WeakMapGet:
+        compileWeakMapGet(node);
+        break;
+
     case ToLowerCase: {
         compileToLowerCase(node);
         break;
index e83829b..23d0db0 100644 (file)
@@ -109,6 +109,12 @@ void printInternal(PrintStream& out, UseKind useKind)
     case SetObjectUse:
         out.print("SetObjectUse");
         return;
+    case WeakMapObjectUse:
+        out.print("WeakMapObjectUse");
+        return;
+    case WeakSetObjectUse:
+        out.print("WeakSetObjectUse");
+        return;
     case ObjectOrOtherUse:
         out.print("ObjectOrOther");
         return;
index 46e2720..796d8b4 100644 (file)
@@ -67,6 +67,8 @@ enum UseKind {
     SymbolUse,
     MapObjectUse,
     SetObjectUse,
+    WeakMapObjectUse,
+    WeakSetObjectUse,
     StringObjectUse,
     StringOrStringObjectUse,
     NotStringVarUse,
@@ -149,6 +151,10 @@ inline SpeculatedType typeFilterFor(UseKind useKind)
         return SpecMapObject;
     case SetObjectUse:
         return SpecSetObject;
+    case WeakMapObjectUse:
+        return SpecWeakMapObject;
+    case WeakSetObjectUse:
+        return SpecWeakSetObject;
     case StringObjectUse:
         return SpecStringObject;
     case StringOrStringObjectUse:
@@ -241,6 +247,8 @@ inline bool isCell(UseKind kind)
     case StringOrStringObjectUse:
     case MapObjectUse:
     case SetObjectUse:
+    case WeakMapObjectUse:
+    case WeakSetObjectUse:
         return true;
     default:
         return false;
index 0340fc0..457451c 100644 (file)
@@ -201,6 +201,7 @@ inline CapabilityLevel canCompile(Node* node)
     case GetMapBucketNext:
     case LoadKeyFromMapBucket:
     case LoadValueFromMapBucket:
+    case WeakMapGet:
     case IsEmpty:
     case IsUndefined:
     case IsBoolean:
@@ -491,6 +492,8 @@ CapabilityLevel canCompile(Graph& graph)
                 case SymbolUse:
                 case MapObjectUse:
                 case SetObjectUse:
+                case WeakMapObjectUse:
+                case WeakSetObjectUse:
                 case FinalObjectUse:
                 case RegExpObjectUse:
                 case ProxyObjectUse:
index 8407439..066cb39 100644 (file)
@@ -1028,6 +1028,9 @@ private:
         case LoadValueFromMapBucket:
             compileLoadValueFromMapBucket();
             break;
+        case WeakMapGet:
+            compileWeakMapGet();
+            break;
         case IsObject:
             compileIsObject();
             break;
@@ -8471,6 +8474,15 @@ private:
         setJSValue(m_out.load64(mapBucket, m_heaps.HashMapBucket_key));
     }
 
+    void compileWeakMapGet()
+    {
+        LValue weakMap = lowWeakMapObject(m_node->child1());
+        LValue object = lowObject(m_node->child2());
+        LValue hash = lowInt32(m_node->child3());
+
+        setJSValue(vmCall(Int64, m_out.operation(operationWeakMapGet), m_callFrame, weakMap, object, hash));
+    }
+
     void compileIsObjectOrNull()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
@@ -12952,6 +12964,20 @@ private:
         speculateSetObject(edge, result);
         return result;
     }
+
+    LValue lowWeakMapObject(Edge edge)
+    {
+        LValue result = lowCell(edge);
+        speculateWeakMapObject(edge, result);
+        return result;
+    }
+
+    LValue lowWeakSetObject(Edge edge)
+    {
+        LValue result = lowCell(edge);
+        speculateWeakSetObject(edge, result);
+        return result;
+    }
     
     LValue lowString(Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation)
     {
@@ -13405,6 +13431,12 @@ private:
         case SetObjectUse:
             speculateSetObject(edge);
             break;
+        case WeakMapObjectUse:
+            speculateWeakMapObject(edge);
+            break;
+        case WeakSetObjectUse:
+            speculateWeakSetObject(edge);
+            break;
         case StringUse:
             speculateString(edge);
             break;
@@ -13800,6 +13832,28 @@ private:
     {
         speculateSetObject(edge, lowCell(edge));
     }
+
+    void speculateWeakMapObject(Edge edge, LValue cell)
+    {
+        FTL_TYPE_CHECK(
+            jsValueValue(cell), edge, SpecWeakMapObject, isNotType(cell, JSWeakMapType));
+    }
+
+    void speculateWeakMapObject(Edge edge)
+    {
+        speculateWeakMapObject(edge, lowCell(edge));
+    }
+
+    void speculateWeakSetObject(Edge edge, LValue cell)
+    {
+        FTL_TYPE_CHECK(
+            jsValueValue(cell), edge, SpecWeakSetObject, isNotType(cell, JSWeakSetType));
+    }
+
+    void speculateWeakSetObject(Edge edge)
+    {
+        speculateWeakSetObject(edge, lowCell(edge));
+    }
     
     void speculateString(Edge edge, LValue cell)
     {
index c23c40b..3dfbef4 100644 (file)
@@ -126,6 +126,7 @@ typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EAapJ)(ExecState*, ArrayAl
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EAapJcpZ)(ExecState*, ArrayAllocationProfile*, const JSValue*, int32_t);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EC)(ExecState*, JSCell*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_ECC)(ExecState*, JSCell*, JSCell*);
+typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_ECCZ)(ExecState*, JSCell*, JSCell*, int32_t);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_ECI)(ExecState*, JSCell*, UniquedStringImpl*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_ECJ)(ExecState*, JSCell*, EncodedJSValue);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_ECZ)(ExecState*, JSCell*, int32_t);
index f98563a..9d12fff 100644 (file)
@@ -153,6 +153,8 @@ const char* intrinsicName(Intrinsic intrinsic)
         return "JSSetBucketNextIntrinsic";
     case JSSetBucketKeyIntrinsic:
         return "JSSetBucketKeyIntrinsic";
+    case JSWeakMapGetIntrinsic:
+        return "JSWeakMapGetIntrinsic";
     case HasOwnPropertyIntrinsic:
         return "HasOwnPropertyIntrinsic";
     case AtomicsAddIntrinsic:
index 4c92a4d..29d95f7 100644 (file)
@@ -89,6 +89,7 @@ enum Intrinsic {
     JSSetBucketHeadIntrinsic,
     JSSetBucketNextIntrinsic,
     JSSetBucketKeyIntrinsic,
+    JSWeakMapGetIntrinsic,
     HasOwnPropertyIntrinsic,
     AtomicsAddIntrinsic,
     AtomicsAndIntrinsic,
index 2f77cd0..eee2a5e 100644 (file)
@@ -94,6 +94,8 @@ enum JSType : uint8_t {
     ProxyObjectType,
     JSMapType,
     JSSetType,
+    JSWeakMapType,
+    JSWeakSetType,
 
     WebAssemblyFunctionType,
 
index 5f8fd21..788aec3 100644 (file)
@@ -38,7 +38,7 @@ public:
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     {
-        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+        return Structure::create(vm, globalObject, prototype, TypeInfo(JSWeakMapType, StructureFlags), info());
     }
 
     static JSWeakMap* create(VM& vm, Structure* structure)
@@ -62,4 +62,16 @@ private:
     static String toStringName(const JSObject*, ExecState*);
 };
 
+inline bool isJSWeakMap(JSCell* from)
+{
+    static_assert(std::is_final<JSWeakMap>::value, "");
+    return from->type() == JSWeakMapType;
+}
+
+inline bool isJSWeakMap(JSValue from)
+{
+    static_assert(std::is_final<JSWeakMap>::value, "");
+    return from.isCell() && from.asCell()->type() == JSWeakMapType;
+}
+
 } // namespace JSC
index 7570f42..228c036 100644 (file)
@@ -38,7 +38,7 @@ public:
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     {
-        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+        return Structure::create(vm, globalObject, prototype, TypeInfo(JSWeakSetType, StructureFlags), info());
     }
 
     static JSWeakSet* create(VM& vm, Structure* structure)
@@ -62,4 +62,16 @@ private:
     static String toStringName(const JSObject*, ExecState*);
 };
 
+inline bool isJSWeakSet(JSCell* from)
+{
+    static_assert(std::is_final<JSWeakSet>::value, "");
+    return from->type() == JSWeakSetType;
+}
+
+inline bool isJSWeakSet(JSValue from)
+{
+    static_assert(std::is_final<JSWeakSet>::value, "");
+    return from.isCell() && from.asCell()->type() == JSWeakSetType;
+}
+
 } // namespace JSC
index 0e51061..85c2003 100644 (file)
@@ -65,6 +65,11 @@ void WeakMapBase::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.reportExtraMemoryVisited(thisObj->m_map.capacity() * (sizeof(JSObject*) + sizeof(WriteBarrier<Unknown>)));
 }
 
+JSValue WeakMapBase::get(JSObject* key)
+{
+    return inlineGet(key);
+}
+
 void WeakMapBase::set(VM& vm, JSObject* key, JSValue value)
 {
     // Here we force the write barrier on the key.
@@ -72,14 +77,6 @@ void WeakMapBase::set(VM& vm, JSObject* key, JSValue value)
     result.iterator->value.set(vm, this, value);
 }
 
-JSValue WeakMapBase::get(JSObject* key)
-{
-    auto iter = m_map.find(key);
-    if (iter == m_map.end())
-        return jsUndefined();
-    return iter->value.get();
-}
-
 bool WeakMapBase::remove(JSObject* key)
 {
     return m_map.remove(key);
index e3bc2af..01c893b 100644 (file)
@@ -38,6 +38,8 @@ public:
 
     void set(VM&, JSObject*, JSValue);
     JSValue get(JSObject*);
+    JSValue inlineGet(JSObject*);
+    JSValue inlineGet(JSObject*, int32_t hash);
     bool remove(JSObject*);
     bool contains(JSObject*);
     void clear();
@@ -57,6 +59,19 @@ protected:
     WeakMapBase(VM&, Structure*);
     static void destroy(JSCell*);
 
+    using KeyWithHash = std::pair<JSObject*, unsigned>;
+    struct HashTranslator {
+        static inline unsigned hash(const KeyWithHash& keyWithHash)
+        {
+            return keyWithHash.second;
+        }
+
+        static inline bool equal(JSObject* key, const KeyWithHash& keyWithHash)
+        {
+            return key == keyWithHash.first;
+        }
+    };
+
     class DeadKeyCleaner : public UnconditionalFinalizer, public WeakReferenceHarvester {
     public:
         WeakMapBase* target();
@@ -70,4 +85,19 @@ protected:
     MapType m_map;
 };
 
+ALWAYS_INLINE JSValue WeakMapBase::inlineGet(JSObject* key)
+{
+    if (auto result = m_map.inlineGet(key))
+        return result.get();
+    return jsUndefined();
+}
+
+ALWAYS_INLINE JSValue WeakMapBase::inlineGet(JSObject* key, int32_t hash)
+{
+    KeyWithHash keyWithHash { key, hash };
+    if (auto result = m_map.inlineGet<HashTranslator>(keyWithHash))
+        return result.get();
+    return jsUndefined();
+}
+
 } // namespace JSC
index 370a78a..5334b13 100644 (file)
@@ -46,25 +46,25 @@ void WeakMapPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     vm.prototypeMap.addPrototype(this);
 
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->deleteKeyword, protoFuncWeakMapDelete, DontEnum, 1);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->get, protoFuncWeakMapGet, DontEnum, 1);
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->get, protoFuncWeakMapGet, DontEnum, 1, JSWeakMapGetIntrinsic);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->has, protoFuncWeakMapHas, DontEnum, 1);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->set, protoFuncWeakMapSet, DontEnum, 2);
 
     putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsString(&vm, "WeakMap"), DontEnum | ReadOnly);
 }
 
-static JSWeakMap* getWeakMap(CallFrame* callFrame, JSValue value)
+ALWAYS_INLINE static JSWeakMap* getWeakMap(CallFrame* callFrame, JSValue value)
 {
     VM& vm = callFrame->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    if (!value.isObject()) {
+    if (UNLIKELY(!value.isObject())) {
         throwTypeError(callFrame, scope, WTF::ASCIILiteral("Called WeakMap function on non-object"));
         return nullptr;
     }
 
-    if (JSWeakMap* weakMap = jsDynamicCast<JSWeakMap*>(vm, value))
-        return weakMap;
+    if (LIKELY(isJSWeakMap(asObject(value))))
+        return jsCast<JSWeakMap*>(value);
 
     throwTypeError(callFrame, scope, WTF::ASCIILiteral("Called WeakMap function on a non-WeakMap object"));
     return nullptr;
@@ -87,7 +87,7 @@ EncodedJSValue JSC_HOST_CALL protoFuncWeakMapGet(CallFrame* callFrame)
     JSValue key = callFrame->argument(0);
     if (!key.isObject())
         return JSValue::encode(jsUndefined());
-    return JSValue::encode(map->get(asObject(key)));
+    return JSValue::encode(map->inlineGet(asObject(key)));
 }
 
 EncodedJSValue JSC_HOST_CALL protoFuncWeakMapHas(CallFrame* callFrame)
index 71be655..5838b4f 100644 (file)
@@ -51,18 +51,18 @@ void WeakSetPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsString(&vm, "WeakSet"), DontEnum | ReadOnly);
 }
 
-static JSWeakSet* getWeakSet(CallFrame* callFrame, JSValue value)
+ALWAYS_INLINE static JSWeakSet* getWeakSet(CallFrame* callFrame, JSValue value)
 {
     VM& vm = callFrame->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    if (!value.isObject()) {
+    if (UNLIKELY(!value.isObject())) {
         throwTypeError(callFrame, scope, WTF::ASCIILiteral("Called WeakSet function on non-object"));
         return nullptr;
     }
 
-    if (JSWeakSet* weakSet = jsDynamicCast<JSWeakSet*>(vm, value))
-        return weakSet;
+    if (LIKELY(isJSWeakSet(asObject(value))))
+        return jsCast<JSWeakSet*>(value);
 
     throwTypeError(callFrame, scope, WTF::ASCIILiteral("Called WeakSet function on a non-WeakSet object"));
     return nullptr;
index 5d704e5..af85f5f 100644 (file)
@@ -1,3 +1,18 @@
+2017-09-03  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DFG] Optimize WeakMap::get by adding intrinsic and fixup
+        https://bugs.webkit.org/show_bug.cgi?id=176010
+
+        Reviewed by Filip Pizlo.
+
+        Add inlineGet method with HashTranslator.
+
+        * wtf/HashMap.h:
+        (WTF::X>::inlineGet const):
+        (WTF::MappedTraits>::inlineGet const):
+        (WTF::MappedTraits>::fastGet const): Deleted.
+        * wtf/LoggingHashMap.h:
+
 2017-09-07  Myles C. Maxfield  <mmaxfield@apple.com>
 
         [PAL] Unify PlatformUserPreferredLanguages.h with Language.h
index fc771b9..1a26c9f 100644 (file)
@@ -106,7 +106,7 @@ public:
     MappedPeekType get(const KeyType&) const;
 
     // Same as get(), but aggressively inlined.
-    MappedPeekType fastGet(const KeyType&) const;
+    MappedPeekType inlineGet(const KeyType&) const;
 
     // Replaces the value but not the key if the key is already present.
     // Return value includes both an iterator to the key location,
@@ -144,6 +144,7 @@ public:
     template<typename HashTranslator, typename T> const_iterator find(const T&) const;
     template<typename HashTranslator, typename T> bool contains(const T&) const;
     template<typename HashTranslator, typename T> MappedPeekType get(const T&) const;
+    template<typename HashTranslator, typename T> MappedPeekType inlineGet(const T&) const;
 
     // An alternate version of add() that finds the object by hashing and comparing
     // with some other type, to avoid the cost of type conversion if the object is already
@@ -306,6 +307,16 @@ auto HashMap<T, U, V, W, X>::get(const TYPE& value) const -> MappedPeekType
 
 template<typename T, typename U, typename V, typename W, typename X>
 template<typename HashTranslator, typename TYPE>
+auto HashMap<T, U, V, W, X>::inlineGet(const TYPE& value) const -> MappedPeekType
+{
+    auto* entry = const_cast<HashTableType&>(m_impl).template inlineLookup<HashMapTranslatorAdapter<KeyValuePairTraits, HashTranslator>>(value);
+    if (!entry)
+        return MappedTraits::peek(MappedTraits::emptyValue());
+    return MappedTraits::peek(entry->value);
+}
+
+template<typename T, typename U, typename V, typename W, typename X>
+template<typename HashTranslator, typename TYPE>
 inline bool HashMap<T, U, V, W, X>::contains(const TYPE& value) const
 {
     return m_impl.template contains<HashMapTranslatorAdapter<KeyValuePairTraits, HashTranslator>>(value);
@@ -407,7 +418,7 @@ inline auto HashMap<T, U, V, W, MappedTraits>::get(const KeyType& key) const ->
 }
 
 template<typename T, typename U, typename V, typename W, typename MappedTraits>
-ALWAYS_INLINE auto HashMap<T, U, V, W, MappedTraits>::fastGet(const KeyType& key) const -> MappedPeekType
+ALWAYS_INLINE auto HashMap<T, U, V, W, MappedTraits>::inlineGet(const KeyType& key) const -> MappedPeekType
 {
     KeyValuePairType* entry = const_cast<HashTableType&>(m_impl).template inlineLookup<IdentityTranslatorType>(key);
     if (!entry)
index e8b13bf..ff8e790 100644 (file)
@@ -152,10 +152,10 @@ public:
         return m_map.get(key);
     }
     
-    MappedPeekType fastGet(const KeyType& key) const
+    MappedPeekType inlineGet(const KeyType& key) const
     {
         find(key);
-        return m_map.fastGet(key);
+        return m_map.inlineGet(key);
     }
     
     template<typename PassedType>
index 95ba120..2720aea 100644 (file)
@@ -1,3 +1,13 @@
+2017-09-03  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DFG] Optimize WeakMap::get by adding intrinsic and fixup
+        https://bugs.webkit.org/show_bug.cgi?id=176010
+
+        Reviewed by Filip Pizlo.
+
+        * platform/network/curl/CurlJobManager.cpp:
+        (WebCore::CurlJobList::finishJobs):
+
 2017-09-10  Zan Dobersek  <zdobersek@igalia.com>
 
         [GStreamer] Drop libgcrypt initialization in webkit_media_clear_key_decrypt_init()
index 64ae090..155e804 100644 (file)
@@ -65,7 +65,7 @@ public:
             if (!m_activeJobs.contains(ticket))
                 continue;
             removeHandle(ticket);
-            notifyResult(m_activeJobs.fastGet(ticket), result);
+            notifyResult(m_activeJobs.inlineGet(ticket), result);
             m_activeJobs.remove(ticket);
         }
     }