Make HasOwnProperty faster
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 20 Sep 2016 01:05:50 +0000 (01:05 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 20 Sep 2016 01:05:50 +0000 (01:05 +0000)
https://bugs.webkit.org/show_bug.cgi?id=161708

Reviewed by Geoffrey Garen.

JSTests:

* microbenchmarks/has-own-property-name-cache.js: Added.
(foo):
* stress/has-own-property-cache-basics.js: Added.
(assert):
(foo):
* stress/has-own-property-name-cache-string-keys.js: Added.
(assert):
(foo):
* stress/has-own-property-name-cache-symbol-keys.js: Added.
(assert):
(foo):
* stress/has-own-property-name-cache-symbols-and-strings.js: Added.
(assert):
(foo):

Source/JavaScriptCore:

This patch adds a cache for HasOwnProperty. The cache holds tuples
of {StructureID, UniquedStringImpl*, boolean} where the boolean indicates
the result of performing hasOwnProperty on an object with StructureID and
UniquedStringImpl*. If the cache contains an item, we can be guaranteed
that it contains the same result as performing hasOwnProperty on an
object O with a given structure and key. To guarantee this, we only add
items into the cache when the Structure of the given item is cacheable.

The caching strategy is simple: when adding new items into the cache,
we will evict any item that was in the location that the new item
is hashed into. We also clear the cache on every GC. This strategy
proves to be successful on speedometer, which sees a cache hit rate
over 90%. This caching strategy is now inlined into the DFG/FTL JITs
by now recognizing hasOwnProperty as an intrinsic with the corresponding
HasOwnProperty node. The goal of the node is to emit inlined code for
the cache lookup to prevent the overhead of the call for the common
case where we get a cache hit.

I'm seeing around a 1% to 1.5% percent improvement on Speedometer on
my machine. Hopefully the perf bots agree with my machine.

This patch also speeds up the microbenchmark I added by 2.5x.

* JavaScriptCore.xcodeproj/project.pbxproj:
* 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/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
(JSC::DFG::SpeculateCellOperand::SpeculateCellOperand):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGValidate.cpp:
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileHasOwnProperty):
* heap/Heap.cpp:
(JSC::Heap::collectImpl):
* jit/JITOperations.h:
* runtime/HasOwnPropertyCache.h: Added.
(JSC::HasOwnPropertyCache::Entry::offsetOfStructureID):
(JSC::HasOwnPropertyCache::Entry::offsetOfImpl):
(JSC::HasOwnPropertyCache::Entry::offsetOfResult):
(JSC::HasOwnPropertyCache::operator delete):
(JSC::HasOwnPropertyCache::create):
(JSC::HasOwnPropertyCache::hash):
(JSC::HasOwnPropertyCache::get):
(JSC::HasOwnPropertyCache::tryAdd):
(JSC::HasOwnPropertyCache::clear):
(JSC::VM::ensureHasOwnPropertyCache):
* runtime/Intrinsic.h:
* runtime/JSObject.h:
* runtime/JSObjectInlines.h:
(JSC::JSObject::hasOwnProperty):
* runtime/ObjectPrototype.cpp:
(JSC::ObjectPrototype::finishCreation):
(JSC::objectProtoFuncHasOwnProperty):
* runtime/Symbol.h:
* runtime/VM.cpp:
* runtime/VM.h:
(JSC::VM::hasOwnPropertyCache):

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

35 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/has-own-property-name-cache.js [new file with mode: 0644]
JSTests/stress/has-own-property-cache-basics.js [new file with mode: 0644]
JSTests/stress/has-own-property-name-cache-string-keys.js [new file with mode: 0644]
JSTests/stress/has-own-property-name-cache-symbol-keys.js [new file with mode: 0644]
JSTests/stress/has-own-property-name-cache-symbols-and-strings.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
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/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.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGValidate.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/heap/Heap.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/runtime/HasOwnPropertyCache.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/JSObjectInlines.h
Source/JavaScriptCore/runtime/ObjectPrototype.cpp
Source/JavaScriptCore/runtime/Symbol.h
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h

index 5f60ecc..48bfcaa 100644 (file)
@@ -1,3 +1,25 @@
+2016-09-19  Saam Barati  <sbarati@apple.com>
+
+        Make HasOwnProperty faster
+        https://bugs.webkit.org/show_bug.cgi?id=161708
+
+        Reviewed by Geoffrey Garen.
+
+        * microbenchmarks/has-own-property-name-cache.js: Added.
+        (foo):
+        * stress/has-own-property-cache-basics.js: Added.
+        (assert):
+        (foo):
+        * stress/has-own-property-name-cache-string-keys.js: Added.
+        (assert):
+        (foo):
+        * stress/has-own-property-name-cache-symbol-keys.js: Added.
+        (assert):
+        (foo):
+        * stress/has-own-property-name-cache-symbols-and-strings.js: Added.
+        (assert):
+        (foo):
+
 2016-09-19  Benjamin Poulain  <bpoulain@apple.com>
 
         [JSC] Make the rounding-related nodes support any type
diff --git a/JSTests/microbenchmarks/has-own-property-name-cache.js b/JSTests/microbenchmarks/has-own-property-name-cache.js
new file mode 100644 (file)
index 0000000..8f6f071
--- /dev/null
@@ -0,0 +1,45 @@
+let objs = [
+    {
+        __proto__: { 
+            foo: 25
+        },
+        bar: 50,
+        baz: 75,
+        jaz: 80,
+    },
+    {
+        __proto__: { 
+            bar: 25
+        },
+        baz: 75,
+        kaz: 80,
+        bar: 50,
+        jaz: 80,
+    },
+    {
+        __proto__: { 
+            bar: 25,
+            jaz: 50
+        },
+        bar: 50,
+        baz: 75,
+        kaz: 80,
+        jaz: 80,
+        foo: 55
+    }
+];
+
+function foo(o) {
+    for (let p in o)
+        o.hasOwnProperty(p);
+
+}
+noInline(foo);
+
+let start = Date.now();
+for (let i = 0; i < 1000000; ++i) {
+    foo(objs[i % objs.length]);
+}
+const verbose = false;
+if (verbose)
+    print(Date.now() - start);
diff --git a/JSTests/stress/has-own-property-cache-basics.js b/JSTests/stress/has-own-property-cache-basics.js
new file mode 100644 (file)
index 0000000..717b481
--- /dev/null
@@ -0,0 +1,31 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion.")
+}
+noInline(assert);
+
+let objs = [
+    {f: 50},
+    {f: 50, g: 70},
+    {g: 50, f: 70},
+    {h: 50, f: 70},
+    {z: 50, f: 70},
+    {k: 50, f: 70},
+];
+
+let s1 = Symbol();
+let s2 = Symbol();
+for (let o of objs)
+    o[s1] = "foo";
+
+function foo(o) {
+    assert(o.hasOwnProperty("f"));
+    assert(!o.hasOwnProperty("ff"));
+    assert(o.hasOwnProperty(s1));
+    assert(!o.hasOwnProperty(s2));
+}
+noInline(foo);
+
+for (let i = 0; i < 40000; ++i) {
+    foo(objs[i % objs.length]);
+}
diff --git a/JSTests/stress/has-own-property-name-cache-string-keys.js b/JSTests/stress/has-own-property-name-cache-string-keys.js
new file mode 100644 (file)
index 0000000..eab029d
--- /dev/null
@@ -0,0 +1,37 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion.");
+}
+noInline(assert);
+
+let objs = [];
+let keyPool = [];
+const numKeys = 800;
+for (let i = 0; i < numKeys; ++i)
+    keyPool.push(i + "foo");
+
+for (let i = 0; i < 10000; i++) {
+    let num = (Math.random() * numKeys) | 0;
+    let o = {};
+    for (let i = 0; i < num; ++i) {
+        o[keyPool[i]] = 25; 
+    }
+    objs.push(o);
+}
+
+function foo(o) {
+    let props = Object.getOwnPropertyNames(o);
+    for (let i = 0; i < props.length; ++i) {
+        let s = props[i];
+        assert(o.hasOwnProperty(s));
+    }
+}
+noInline(foo);
+
+let start = Date.now();
+for (let o of objs) {
+    foo(o);
+}
+const verbose = false;
+if (verbose)
+    print(Date.now() - start);
diff --git a/JSTests/stress/has-own-property-name-cache-symbol-keys.js b/JSTests/stress/has-own-property-name-cache-symbol-keys.js
new file mode 100644 (file)
index 0000000..bb99391
--- /dev/null
@@ -0,0 +1,34 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion.");
+}
+noInline(assert);
+
+let objs = [];
+let symbolPool = [];
+const numSymbols = 800;
+for (let i = 0; i < numSymbols; ++i)
+    symbolPool.push(Symbol());
+
+for (let i = 0; i < 10000; i++) {
+    let num = (Math.random() * numSymbols) | 0;
+    let o = {};
+    for (let i = 0; i < num; ++i) {
+        o[symbolPool[i]] = 25; 
+    }
+    objs.push(o);
+}
+
+function foo(o) {
+    let props = Object.getOwnPropertySymbols(o);
+    for (let i = 0; i < props.length; ++i) {
+        let s = props[i];
+        assert(o.hasOwnProperty(s));
+    }
+}
+noInline(foo);
+
+let start = Date.now();
+for (let o of objs) {
+    foo(o);
+}
diff --git a/JSTests/stress/has-own-property-name-cache-symbols-and-strings.js b/JSTests/stress/has-own-property-name-cache-symbols-and-strings.js
new file mode 100644 (file)
index 0000000..7832840
--- /dev/null
@@ -0,0 +1,45 @@
+
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion.");
+}
+noInline(assert);
+
+let objs = [];
+let keyPool = [];
+let symbolPool = [];
+const numKeys = 500;
+for (let i = 0; i < numKeys; ++i) {
+    keyPool.push(i + "foo");
+    symbolPool.push(Symbol("Foo"));
+}
+
+for (let i = 0; i < 10000; i++) {
+    let num = (Math.random() * numKeys) | 0;
+    let o = {};
+    for (let i = 0; i < num; ++i) {
+        o[keyPool[i]] = 25; 
+        o[symbolPool[i]] = 40; 
+    }
+    objs.push(o);
+}
+
+let time = 0;
+function foo(o) {
+    let props = Object.getOwnPropertyNames(o);
+    props.push(...Object.getOwnPropertySymbols(o));
+    let start = Date.now();
+    for (let i = 0; i < props.length; ++i) {
+        let s = props[i];
+        assert(o.hasOwnProperty(s));
+    }
+    time += Date.now() - start;
+}
+noInline(foo);
+
+for (let o of objs) {
+    foo(o);
+}
+const verbose = false;
+if (verbose)
+    print(time);
index 9af406a..6ee01ae 100644 (file)
@@ -1,3 +1,90 @@
+2016-09-19  Saam Barati  <sbarati@apple.com>
+
+        Make HasOwnProperty faster
+        https://bugs.webkit.org/show_bug.cgi?id=161708
+
+        Reviewed by Geoffrey Garen.
+
+        This patch adds a cache for HasOwnProperty. The cache holds tuples
+        of {StructureID, UniquedStringImpl*, boolean} where the boolean indicates
+        the result of performing hasOwnProperty on an object with StructureID and
+        UniquedStringImpl*. If the cache contains an item, we can be guaranteed
+        that it contains the same result as performing hasOwnProperty on an
+        object O with a given structure and key. To guarantee this, we only add
+        items into the cache when the Structure of the given item is cacheable.
+
+        The caching strategy is simple: when adding new items into the cache,
+        we will evict any item that was in the location that the new item
+        is hashed into. We also clear the cache on every GC. This strategy
+        proves to be successful on speedometer, which sees a cache hit rate
+        over 90%. This caching strategy is now inlined into the DFG/FTL JITs
+        by now recognizing hasOwnProperty as an intrinsic with the corresponding
+        HasOwnProperty node. The goal of the node is to emit inlined code for
+        the cache lookup to prevent the overhead of the call for the common
+        case where we get a cache hit.
+
+        I'm seeing around a 1% to 1.5% percent improvement on Speedometer on
+        my machine. Hopefully the perf bots agree with my machine.
+
+        This patch also speeds up the microbenchmark I added by 2.5x.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * 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/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        (JSC::DFG::SpeculateCellOperand::SpeculateCellOperand):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGValidate.cpp:
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileHasOwnProperty):
+        * heap/Heap.cpp:
+        (JSC::Heap::collectImpl):
+        * jit/JITOperations.h:
+        * runtime/HasOwnPropertyCache.h: Added.
+        (JSC::HasOwnPropertyCache::Entry::offsetOfStructureID):
+        (JSC::HasOwnPropertyCache::Entry::offsetOfImpl):
+        (JSC::HasOwnPropertyCache::Entry::offsetOfResult):
+        (JSC::HasOwnPropertyCache::operator delete):
+        (JSC::HasOwnPropertyCache::create):
+        (JSC::HasOwnPropertyCache::hash):
+        (JSC::HasOwnPropertyCache::get):
+        (JSC::HasOwnPropertyCache::tryAdd):
+        (JSC::HasOwnPropertyCache::clear):
+        (JSC::VM::ensureHasOwnPropertyCache):
+        * runtime/Intrinsic.h:
+        * runtime/JSObject.h:
+        * runtime/JSObjectInlines.h:
+        (JSC::JSObject::hasOwnProperty):
+        * runtime/ObjectPrototype.cpp:
+        (JSC::ObjectPrototype::finishCreation):
+        (JSC::objectProtoFuncHasOwnProperty):
+        * runtime/Symbol.h:
+        * runtime/VM.cpp:
+        * runtime/VM.h:
+        (JSC::VM::hasOwnPropertyCache):
+
 2016-09-19  Benjamin Poulain  <bpoulain@apple.com>
 
         [JSC] Make the rounding-related nodes support any type
index 6fc35b2..0fc4d4a 100644 (file)
                79CFC6F01C33B10000C768EA /* LLIntPCRanges.h in Headers */ = {isa = PBXBuildFile; fileRef = 79CFC6EF1C33B10000C768EA /* LLIntPCRanges.h */; settings = {ATTRIBUTES = (Private, ); }; };
                79D5CD5A1C1106A900CECA07 /* SamplingProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79D5CD581C1106A900CECA07 /* SamplingProfiler.cpp */; };
                79D5CD5B1C1106A900CECA07 /* SamplingProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 79D5CD591C1106A900CECA07 /* SamplingProfiler.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               79DFCBDB1D88C59600527D03 /* HasOwnPropertyCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 79DFCBDA1D88C59600527D03 /* HasOwnPropertyCache.h */; settings = {ATTRIBUTES = (Private, ); }; };
                79EE0BFF1B4AFB85000385C9 /* VariableEnvironment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79EE0BFD1B4AFB85000385C9 /* VariableEnvironment.cpp */; };
                79EE0C001B4AFB85000385C9 /* VariableEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = 79EE0BFE1B4AFB85000385C9 /* VariableEnvironment.h */; settings = {ATTRIBUTES = (Private, ); }; };
                79F8FC1E1B9FED0F00CA66AB /* DFGMaximalFlushInsertionPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79F8FC1C1B9FED0F00CA66AB /* DFGMaximalFlushInsertionPhase.cpp */; };
                79CFC6EF1C33B10000C768EA /* LLIntPCRanges.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLIntPCRanges.h; path = llint/LLIntPCRanges.h; sourceTree = "<group>"; };
                79D5CD581C1106A900CECA07 /* SamplingProfiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SamplingProfiler.cpp; sourceTree = "<group>"; };
                79D5CD591C1106A900CECA07 /* SamplingProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SamplingProfiler.h; sourceTree = "<group>"; };
+               79DFCBDA1D88C59600527D03 /* HasOwnPropertyCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HasOwnPropertyCache.h; sourceTree = "<group>"; };
                79EE0BFD1B4AFB85000385C9 /* VariableEnvironment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VariableEnvironment.cpp; sourceTree = "<group>"; };
                79EE0BFE1B4AFB85000385C9 /* VariableEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VariableEnvironment.h; sourceTree = "<group>"; };
                79F8FC1C1B9FED0F00CA66AB /* DFGMaximalFlushInsertionPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGMaximalFlushInsertionPhase.cpp; path = dfg/DFGMaximalFlushInsertionPhase.cpp; sourceTree = "<group>"; };
                                BC337BDE0E1AF0B80076918A /* GetterSetter.h */,
                                79A0907D1D768465008B889B /* HashMapImpl.cpp */,
                                79A0907E1D768465008B889B /* HashMapImpl.h */,
+                               79DFCBDA1D88C59600527D03 /* HasOwnPropertyCache.h */,
                                933A349D038AE80F008635CE /* Identifier.cpp */,
                                933A349A038AE7C6008635CE /* Identifier.h */,
                                8606DDE918DA44AB00A383D0 /* IdentifierInlines.h */,
                                0FF42748158EBE91004CB9FF /* udis86_syn.h in Headers */,
                                0FF42749158EBE91004CB9FF /* udis86_types.h in Headers */,
                                A7E5AB391799E4B200D2833D /* UDis86Disassembler.h in Headers */,
+                               79DFCBDB1D88C59600527D03 /* HasOwnPropertyCache.h in Headers */,
                                A7A8AF4117ADB5F3005AB174 /* Uint16Array.h in Headers */,
                                866739D313BFDE710023D87C /* Uint16WithFraction.h in Headers */,
                                A7A8AF4217ADB5F3005AB174 /* Uint32Array.h in Headers */,
index 9275470..8c74126 100644 (file)
@@ -2665,6 +2665,12 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         forNode(node).setType(SpecBoolean);
         break;
     }
+
+    case HasOwnProperty: {
+        clobberWorld(node->origin.semantic, clobberLimit);
+        forNode(node).setType(SpecBoolean);
+        break;
+    }
             
     case GetEnumerableLength: {
         forNode(node).setType(SpecInt32Only);
index 71a7461..9ac17df 100644 (file)
@@ -2561,6 +2561,26 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         return true;
     }
 
+    case HasOwnPropertyIntrinsic: {
+        if (argumentCountIncludingThis != 2)
+            return false;
+
+        // This can be racy, that's fine. We know that once we observe that this is created,
+        // that it will never be destroyed until the VM is destroyed. It's unlikely that
+        // we'd ever get to the point where we inline this as an intrinsic without the
+        // cache being created, however, it's possible if we always throw exceptions inside
+        // hasOwnProperty.
+        if (!m_vm->hasOwnPropertyCache())
+            return false;
+
+        insertChecks();
+        Node* object = get(virtualRegisterForArgument(0, registerOffset));
+        Node* key = get(virtualRegisterForArgument(1, registerOffset));
+        Node* result = addToGraph(HasOwnProperty, object, key);
+        set(VirtualRegister(resultOperand), result);
+        return true;
+    }
+
     default:
         return false;
     }
index f90c785..c1c3fdd 100644 (file)
@@ -501,6 +501,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case ConstructForwardVarargs:
     case ToPrimitive:
     case In:
+    case HasOwnProperty:
     case ValueAdd:
     case SetFunctionName:
     case GetDynamicVar:
index 83b2be8..2dcf55d 100644 (file)
@@ -173,6 +173,7 @@ bool doesGC(Graph& graph, Node* node)
     case ToString:
     case CallStringConstructor:
     case In:
+    case HasOwnProperty:
     case Jump:
     case Branch:
     case Switch:
index f68b171..6ee5142 100644 (file)
@@ -1311,6 +1311,22 @@ private:
             break;
         }
 
+        case HasOwnProperty: {
+            fixEdge<ObjectUse>(node->child1());
+#if CPU(X86) && USE(JSVALUE32_64)
+            // We don't have enough registers to do anything interesting on x86.
+            fixEdge<UntypedUse>(node->child2());
+#else
+            if (node->child2()->shouldSpeculateString())
+                fixEdge<StringUse>(node->child2());
+            else if (node->child2()->shouldSpeculateSymbol())
+                fixEdge<SymbolUse>(node->child2());
+            else
+                fixEdge<UntypedUse>(node->child2());
+#endif
+            break;
+        }
+
         case Check: {
             m_graph.doToChildren(
                 node,
index 050b673..1f35eb2 100644 (file)
@@ -331,6 +331,7 @@ namespace JSC { namespace DFG {
     macro(ProfileType, NodeMustGenerate) \
     macro(ProfileControlFlow, NodeMustGenerate) \
     macro(SetFunctionName, NodeMustGenerate) \
+    macro(HasOwnProperty, NodeResultBoolean) \
     \
     macro(CreateActivation, NodeResultJS) \
     \
index ce546eb..f3ef52e 100644 (file)
@@ -42,6 +42,7 @@
 #include "FTLForOSREntryJITCode.h"
 #include "FTLOSREntry.h"
 #include "GetterSetter.h"
+#include "HasOwnPropertyCache.h"
 #include "HostCallReturnValue.h"
 #include "Interpreter.h"
 #include "JIT.h"
@@ -1673,6 +1674,28 @@ int32_t JIT_OPERATION operationSizeOfVarargs(ExecState* exec, EncodedJSValue enc
     return sizeOfVarargs(exec, arguments, firstVarArgOffset);
 }
 
+int32_t JIT_OPERATION operationHasOwnProperty(ExecState* exec, JSObject* thisObject, EncodedJSValue encodedKey)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue key = JSValue::decode(encodedKey);
+    Identifier propertyName = key.toPropertyKey(exec);
+    if (UNLIKELY(scope.exception()))
+        return false;
+
+    PropertySlot slot(thisObject, PropertySlot::InternalMethodType::GetOwnProperty);
+    bool result = thisObject->hasOwnProperty(exec, propertyName.impl(), slot);
+    if (UNLIKELY(scope.exception()))
+        return false;
+
+    HasOwnPropertyCache* hasOwnPropertyCache = vm.hasOwnPropertyCache();
+    ASSERT(hasOwnPropertyCache);
+    hasOwnPropertyCache->tryAdd(vm, slot, thisObject, propertyName.impl(), result);
+    return result;
+}
+
 void JIT_OPERATION operationLoadVarargs(ExecState* exec, int32_t firstElementDest, EncodedJSValue encodedArguments, int32_t offset, int32_t length, int32_t mandatoryMinimum)
 {
     VM& vm = exec->vm();
index 6ab7675..527bfe5 100644 (file)
@@ -177,6 +177,8 @@ void JIT_OPERATION operationThrowStackOverflowForVarargs(ExecState*) WTF_INTERNA
 int32_t JIT_OPERATION operationSizeOfVarargs(ExecState*, EncodedJSValue arguments, int32_t firstVarArgOffset);
 void JIT_OPERATION operationLoadVarargs(ExecState*, int32_t firstElementDest, EncodedJSValue arguments, int32_t offset, int32_t length, int32_t mandatoryMinimum);
 
+int32_t JIT_OPERATION operationHasOwnProperty(ExecState*, JSObject*, EncodedJSValue);
+
 JSCell* JIT_OPERATION operationResolveScope(ExecState*, JSScope*, UniquedStringImpl*);
 EncodedJSValue JIT_OPERATION operationGetDynamicVar(ExecState*, JSObject* scope, UniquedStringImpl*, unsigned);
 void JIT_OPERATION operationPutDynamicVar(ExecState*, JSObject* scope, EncodedJSValue, UniquedStringImpl*, unsigned);
index 0694e13..06db40e 100644 (file)
@@ -910,6 +910,10 @@ private:
             setPrediction(SpecBoolean);
             break;
 
+        case HasOwnProperty:
+            setPrediction(SpecBoolean);
+            break;
+
         case GetEnumerableLength: {
             setPrediction(SpecInt32Only);
             break;
index bd069b9..22b9340 100644 (file)
@@ -284,6 +284,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case NewStringObject:
     case MakeRope:
     case In:
+    case HasOwnProperty:
     case CreateActivation:
     case CreateDirectArguments:
     case CreateScopedArguments:
index 7429681..47ead30 100644 (file)
@@ -982,6 +982,11 @@ public:
         m_jit.setupArgumentsWithExecState(TrustedImmPtr(index), arg1);
         return appendCallSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(Z_JITOperation_EOI operation, GPRReg result, GPRReg obj, GPRReg impl)
+    {
+        m_jit.setupArgumentsWithExecState(obj, impl);
+        return appendCallSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(P_JITOperation_ESt operation, GPRReg result, Structure* structure)
     {
         m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure));
@@ -1309,19 +1314,21 @@ public:
 
 
 #if USE(JSVALUE64)
-
+    JITCompiler::Call callOperation(Z_JITOperation_EOJ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2);
+        return appendCallSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(C_JITOperation_ECJZ operation, GPRReg result, GPRReg arg1, GPRReg arg2, GPRReg arg3)
     {
         m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
         return appendCallSetResult(operation, result);
     }
-
     JITCompiler::Call callOperation(J_JITOperation_EJJMic operation, JSValueRegs result, JSValueRegs arg1, JSValueRegs arg2, TrustedImmPtr mathIC)
     {
         m_jit.setupArgumentsWithExecState(arg1.gpr(), arg2.gpr(), mathIC);
         return appendCallSetResult(operation, result.gpr());
     }
-
     JITCompiler::Call callOperation(J_JITOperation_EJJI operation, GPRReg result, GPRReg arg1, GPRReg arg2, UniquedStringImpl* uid)
     {
         m_jit.setupArgumentsWithExecState(arg1, arg2, TrustedImmPtr(uid));
@@ -1749,7 +1756,11 @@ public:
         return appendCallSetResult(operation, result);
     }
 #else // USE(JSVALUE32_64)
-
+    JITCompiler::Call callOperation(Z_JITOperation_EOJ operation, GPRReg result, GPRReg arg1, JSValueRegs arg2)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2.payloadGPR(), arg2.tagGPR());
+        return appendCallSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(C_JITOperation_ECJZ operation, GPRReg result, GPRReg arg1, JSValueRegs arg2, GPRReg arg3)
     {
         m_jit.setupArgumentsWithExecState(arg1, arg2.payloadGPR(), arg2.tagGPR(), arg3);
@@ -3522,6 +3533,8 @@ private:
 };
 
 class SpeculateCellOperand {
+    WTF_MAKE_NONCOPYABLE(SpeculateCellOperand);
+
 public:
     explicit SpeculateCellOperand(SpeculativeJIT* jit, Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation)
         : m_jit(jit)
@@ -3536,6 +3549,16 @@ public:
             gpr();
     }
 
+    explicit SpeculateCellOperand(SpeculateCellOperand&& other)
+    {
+        m_jit = other.m_jit;
+        m_edge = other.m_edge;
+        m_gprOrInvalid = other.m_gprOrInvalid;
+
+        other.m_gprOrInvalid = InvalidGPRReg;
+        other.m_edge = Edge();
+    }
+
     ~SpeculateCellOperand()
     {
         if (!m_edge)
index ed00635..ccbdb26 100644 (file)
@@ -37,6 +37,7 @@
 #include "DFGSlowPathGenerator.h"
 #include "DirectArguments.h"
 #include "GetterSetter.h"
+#include "HasOwnPropertyCache.h"
 #include "HashMapImpl.h"
 #include "JSEnvironmentRecord.h"
 #include "JSLexicalEnvironment.h"
@@ -4888,6 +4889,122 @@ void SpeculativeJIT::compile(Node* node)
         compileIn(node);
         break;
 
+    case HasOwnProperty: {
+#if CPU(X86)
+        ASSERT(node->child2().useKind() == UntypedUse);
+        SpeculateCellOperand object(this, node->child1());
+        JSValueOperand key(this, node->child2());
+        GPRTemporary result(this, Reuse, object);
+
+        JSValueRegs keyRegs = key.jsValueRegs();
+        GPRReg objectGPR = object.gpr();
+        GPRReg resultGPR = result.gpr();
+        flushRegisters();
+        callOperation(operationHasOwnProperty, resultGPR, objectGPR, keyRegs);
+        booleanResult(resultGPR, node);
+#else
+        SpeculateCellOperand object(this, node->child1());
+        GPRTemporary uniquedStringImpl(this);
+        GPRTemporary temp(this);
+        GPRTemporary hash(this);
+        GPRTemporary structureID(this);
+        GPRTemporary result(this);
+
+        Optional<SpeculateCellOperand> keyAsCell;
+        Optional<JSValueOperand> keyAsValue;
+        JSValueRegs keyRegs;
+        if (node->child2().useKind() == UntypedUse) {
+            keyAsValue = JSValueOperand(this, node->child2());
+            keyRegs = keyAsValue->jsValueRegs();
+        } else {
+            ASSERT(node->child2().useKind() == StringUse || node->child2().useKind() == SymbolUse);
+            keyAsCell = SpeculateCellOperand(this, node->child2());
+            keyRegs = JSValueRegs::payloadOnly(keyAsCell->gpr());
+        }
+
+        GPRReg objectGPR = object.gpr();
+        GPRReg implGPR = uniquedStringImpl.gpr();
+        GPRReg tempGPR = temp.gpr();
+        GPRReg hashGPR = hash.gpr();
+        GPRReg structureIDGPR = structureID.gpr();
+        GPRReg resultGPR = result.gpr();
+
+        speculateObject(node->child1());
+
+        MacroAssembler::JumpList slowPath;
+        switch (node->child2().useKind()) {
+        case SymbolUse: {
+            speculateSymbol(node->child2(), keyRegs.payloadGPR());
+            m_jit.loadPtr(MacroAssembler::Address(keyRegs.payloadGPR(), Symbol::offsetOfSymbolImpl()), implGPR);
+            break;
+        }
+        case StringUse: {
+            speculateString(node->child2(), keyRegs.payloadGPR());
+            m_jit.loadPtr(MacroAssembler::Address(keyRegs.payloadGPR(), JSString::offsetOfValue()), implGPR);
+            slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, implGPR));
+            slowPath.append(m_jit.branchTest32(
+                MacroAssembler::Zero, MacroAssembler::Address(implGPR, StringImpl::flagsOffset()),
+                MacroAssembler::TrustedImm32(StringImpl::flagIsAtomic())));
+            break;
+        }
+        case UntypedUse: {
+            slowPath.append(m_jit.branchIfNotCell(keyRegs));
+            auto isNotString = m_jit.branchIfNotString(keyRegs.payloadGPR());
+            m_jit.loadPtr(MacroAssembler::Address(keyRegs.payloadGPR(), JSString::offsetOfValue()), implGPR);
+            slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, implGPR));
+            slowPath.append(m_jit.branchTest32(
+                MacroAssembler::Zero, MacroAssembler::Address(implGPR, StringImpl::flagsOffset()),
+                MacroAssembler::TrustedImm32(StringImpl::flagIsAtomic())));
+            auto hasUniquedImpl = m_jit.jump();
+
+            isNotString.link(&m_jit);
+            slowPath.append(m_jit.branchIfNotSymbol(keyRegs.payloadGPR()));
+            m_jit.loadPtr(MacroAssembler::Address(keyRegs.payloadGPR(), Symbol::offsetOfSymbolImpl()), implGPR);
+
+            hasUniquedImpl.link(&m_jit);
+            break;
+        }
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+
+        // Note that we don't test if the hash is zero here. AtomicStringImpl's can't have a zero
+        // hash, however, a SymbolImpl may. But, because this is a cache, we don't care. We only
+        // ever load the result from the cache if the cache entry matches what we are querying for.
+        // So we either get super lucky and use zero for the hash and somehow collide with the entity
+        // we're looking for, or we realize we're comparing against another entity, and go to the
+        // slow path anyways.
+        m_jit.load32(MacroAssembler::Address(implGPR, UniquedStringImpl::flagsOffset()), hashGPR);
+        m_jit.urshift32(MacroAssembler::TrustedImm32(StringImpl::s_flagCount), hashGPR);
+        m_jit.load32(MacroAssembler::Address(objectGPR, JSCell::structureIDOffset()), structureIDGPR);
+        m_jit.add32(structureIDGPR, hashGPR);
+        m_jit.and32(TrustedImm32(HasOwnPropertyCache::mask), hashGPR);
+        m_jit.mul32(TrustedImm32(sizeof(HasOwnPropertyCache::Entry)), hashGPR, hashGPR);
+        ASSERT(m_jit.vm()->hasOwnPropertyCache());
+        m_jit.move(TrustedImmPtr(m_jit.vm()->hasOwnPropertyCache()), tempGPR);
+        slowPath.append(m_jit.branchPtr(MacroAssembler::NotEqual, 
+            MacroAssembler::BaseIndex(tempGPR, hashGPR, MacroAssembler::TimesOne, HasOwnPropertyCache::Entry::offsetOfImpl()), implGPR));
+        m_jit.load8(MacroAssembler::BaseIndex(tempGPR, hashGPR, MacroAssembler::TimesOne, HasOwnPropertyCache::Entry::offsetOfResult()), resultGPR);
+        m_jit.load32(MacroAssembler::BaseIndex(tempGPR, hashGPR, MacroAssembler::TimesOne, HasOwnPropertyCache::Entry::offsetOfStructureID()), tempGPR);
+        slowPath.append(m_jit.branch32(MacroAssembler::NotEqual, tempGPR, structureIDGPR));
+        auto done = m_jit.jump();
+
+        slowPath.link(&m_jit);
+        silentSpillAllRegisters(resultGPR);
+        if (node->child2().useKind() != UntypedUse) {
+            m_jit.move(MacroAssembler::TrustedImm32(JSValue::CellTag), tempGPR);
+            keyRegs = JSValueRegs(tempGPR, keyRegs.payloadGPR());
+        }
+        callOperation(operationHasOwnProperty, resultGPR, objectGPR, keyRegs);
+        silentFillAllRegisters(resultGPR);
+        m_jit.exceptionCheck();
+
+        done.link(&m_jit);
+        booleanResult(resultGPR, node);
+#endif // CPU(X86)
+        break;
+    }
+
     case StoreBarrier: {
         compileStoreBarrier(node);
         break;
index ea3eec9..ab28862 100644 (file)
@@ -36,6 +36,7 @@
 #include "DFGSlowPathGenerator.h"
 #include "DirectArguments.h"
 #include "GetterSetter.h"
+#include "HasOwnPropertyCache.h"
 #include "JSCInlines.h"
 #include "JSEnvironmentRecord.h"
 #include "JSLexicalEnvironment.h"
@@ -4628,7 +4629,7 @@ void SpeculativeJIT::compile(Node* node)
         m_jit.loadPtr(MacroAssembler::Address(inputGPR, JSString::offsetOfValue()), resultGPR);
         slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, resultGPR));
         m_jit.load32(MacroAssembler::Address(resultGPR, StringImpl::flagsOffset()), resultGPR);
-        m_jit.urshift64(MacroAssembler::TrustedImm32(StringImpl::s_flagCount), resultGPR);
+        m_jit.urshift32(MacroAssembler::TrustedImm32(StringImpl::s_flagCount), resultGPR);
         slowPath.append(m_jit.branchTest32(MacroAssembler::Zero, resultGPR));
         done.append(m_jit.jump());
 
@@ -4935,6 +4936,106 @@ void SpeculativeJIT::compile(Node* node)
     case In:
         compileIn(node);
         break;
+
+    case HasOwnProperty: {
+        SpeculateCellOperand object(this, node->child1());
+        GPRTemporary uniquedStringImpl(this);
+        GPRTemporary temp(this);
+        GPRTemporary hash(this);
+        GPRTemporary structureID(this);
+        GPRTemporary result(this);
+
+        Optional<SpeculateCellOperand> keyAsCell;
+        Optional<JSValueOperand> keyAsValue;
+        GPRReg keyGPR;
+        if (node->child2().useKind() == UntypedUse) {
+            keyAsValue = JSValueOperand(this, node->child2());
+            keyGPR = keyAsValue->gpr();
+        } else {
+            ASSERT(node->child2().useKind() == StringUse || node->child2().useKind() == SymbolUse);
+            keyAsCell = SpeculateCellOperand(this, node->child2());
+            keyGPR = keyAsCell->gpr();
+        }
+
+        GPRReg objectGPR = object.gpr();
+        GPRReg implGPR = uniquedStringImpl.gpr();
+        GPRReg tempGPR = temp.gpr();
+        GPRReg hashGPR = hash.gpr();
+        GPRReg structureIDGPR = structureID.gpr();
+        GPRReg resultGPR = result.gpr();
+
+        speculateObject(node->child1());
+
+        MacroAssembler::JumpList slowPath;
+        switch (node->child2().useKind()) {
+        case SymbolUse: {
+            speculateSymbol(node->child2(), keyGPR);
+            m_jit.loadPtr(MacroAssembler::Address(keyGPR, Symbol::offsetOfSymbolImpl()), implGPR);
+            break;
+        }
+        case StringUse: {
+            speculateString(node->child2(), keyGPR);
+            m_jit.loadPtr(MacroAssembler::Address(keyGPR, JSString::offsetOfValue()), implGPR);
+            slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, implGPR));
+            slowPath.append(m_jit.branchTest32(
+                MacroAssembler::Zero, MacroAssembler::Address(implGPR, StringImpl::flagsOffset()),
+                MacroAssembler::TrustedImm32(StringImpl::flagIsAtomic())));
+            break;
+        }
+        case UntypedUse: {
+            slowPath.append(m_jit.branchIfNotCell(JSValueRegs(keyGPR)));
+            auto isNotString = m_jit.branchIfNotString(keyGPR);
+            m_jit.loadPtr(MacroAssembler::Address(keyGPR, JSString::offsetOfValue()), implGPR);
+            slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, implGPR));
+            slowPath.append(m_jit.branchTest32(
+                MacroAssembler::Zero, MacroAssembler::Address(implGPR, StringImpl::flagsOffset()),
+                MacroAssembler::TrustedImm32(StringImpl::flagIsAtomic())));
+            auto hasUniquedImpl = m_jit.jump();
+
+            isNotString.link(&m_jit);
+            slowPath.append(m_jit.branchIfNotSymbol(keyGPR));
+            m_jit.loadPtr(MacroAssembler::Address(keyGPR, Symbol::offsetOfSymbolImpl()), implGPR);
+
+            hasUniquedImpl.link(&m_jit);
+            break;
+        }
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+
+        // Note that we don't test if the hash is zero here. AtomicStringImpl's can't have a zero
+        // hash, however, a SymbolImpl may. But, because this is a cache, we don't care. We only
+        // ever load the result from the cache if the cache entry matches what we are querying for.
+        // So we either get super lucky and use zero for the hash and somehow collide with the entity
+        // we're looking for, or we realize we're comparing against another entity, and go to the
+        // slow path anyways.
+        m_jit.load32(MacroAssembler::Address(implGPR, UniquedStringImpl::flagsOffset()), hashGPR);
+        m_jit.urshift32(MacroAssembler::TrustedImm32(StringImpl::s_flagCount), hashGPR);
+        m_jit.load32(MacroAssembler::Address(objectGPR, JSCell::structureIDOffset()), structureIDGPR);
+        m_jit.add32(structureIDGPR, hashGPR);
+        m_jit.and32(TrustedImm32(HasOwnPropertyCache::mask), hashGPR);
+        static_assert(sizeof(HasOwnPropertyCache::Entry) == 16, "Strong assumption of that here.");
+        m_jit.lshift32(TrustedImm32(4), hashGPR);
+        ASSERT(m_jit.vm()->hasOwnPropertyCache());
+        m_jit.move(TrustedImmPtr(m_jit.vm()->hasOwnPropertyCache()), tempGPR);
+        slowPath.append(m_jit.branchPtr(MacroAssembler::NotEqual, 
+            MacroAssembler::BaseIndex(tempGPR, hashGPR, MacroAssembler::TimesOne, HasOwnPropertyCache::Entry::offsetOfImpl()), implGPR));
+        m_jit.load8(MacroAssembler::BaseIndex(tempGPR, hashGPR, MacroAssembler::TimesOne, HasOwnPropertyCache::Entry::offsetOfResult()), resultGPR);
+        m_jit.load32(MacroAssembler::BaseIndex(tempGPR, hashGPR, MacroAssembler::TimesOne, HasOwnPropertyCache::Entry::offsetOfStructureID()), tempGPR);
+        slowPath.append(m_jit.branch32(MacroAssembler::NotEqual, tempGPR, structureIDGPR));
+        auto done = m_jit.jump();
+
+        slowPath.link(&m_jit);
+        silentSpillAllRegisters(resultGPR);
+        callOperation(operationHasOwnProperty, resultGPR, objectGPR, keyGPR);
+        silentFillAllRegisters(resultGPR);
+        m_jit.exceptionCheck();
+
+        done.link(&m_jit);
+        m_jit.or32(TrustedImm32(ValueFalse), resultGPR);
+        jsValueResult(resultGPR, node, DataFormatJSBoolean);
+        break;
+    }
         
     case CountExecution:
         m_jit.add64(TrustedImm32(1), MacroAssembler::AbsoluteAddress(node->executionCounter()->address()));
index a4ebf60..918d8ad 100644 (file)
@@ -309,6 +309,10 @@ public:
                     // while the inline one will not take a storage child at all.
                     // https://bugs.webkit.org/show_bug.cgi?id=159602
                     break;
+                case HasOwnProperty: {
+                    VALIDATE((node), !!m_graph.m_vm.hasOwnPropertyCache());
+                    break;
+                }
                 default:
                     break;
                 }
index 398ce97..48e2642 100644 (file)
 #include "B3Value.h"
 #include "DFGArrayMode.h"
 #include "FTLAbstractHeap.h"
+#include "HasOwnPropertyCache.h"
 #include "IndexingType.h"
 #include "JSMap.h"
 #include "JSSet.h"
+#include "Symbol.h"
 
 namespace JSC { namespace FTL {
 
@@ -110,6 +112,7 @@ namespace JSC { namespace FTL {
     macro(HashMapImpl_buffer,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfBuffer()) \
     macro(HashMapBucket_value, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfValue()) \
     macro(HashMapBucket_key, HashMapBucket<HashMapBucketDataKeyValue>::offsetOfKey()) \
+    macro(Symbol_symbolImpl, Symbol::offsetOfSymbolImpl()) \
 
 #define FOR_EACH_INDEXED_ABSTRACT_HEAP(macro) \
     macro(DirectArguments_storage, DirectArguments::storageOffset(), sizeof(EncodedJSValue)) \
@@ -128,7 +131,8 @@ namespace JSC { namespace FTL {
     macro(scopedArgumentsTableArguments, 0, sizeof(int32_t)) \
     macro(singleCharacterStrings, 0, sizeof(JSString*)) \
     macro(structureTable, 0, sizeof(Structure*)) \
-    macro(variables, 0, sizeof(Register))
+    macro(variables, 0, sizeof(Register)) \
+    macro(HasOwnPropertyCache, 0, sizeof(HasOwnPropertyCache::Entry)) \
     
 #define FOR_EACH_NUMBERED_ABSTRACT_HEAP(macro) \
     macro(properties)
index 38fdfbc..cad45fd 100644 (file)
@@ -181,6 +181,7 @@ inline CapabilityLevel canCompile(Node* node)
     case ThrowReferenceError:
     case Unreachable:
     case In:
+    case HasOwnProperty:
     case IsCellWithType:
     case MapHash:
     case GetMapBucket:
index f4f1dab..a7b6ebb 100644 (file)
@@ -632,6 +632,9 @@ private:
         case In:
             compileIn();
             break;
+        case HasOwnProperty:
+            compileHasOwnProperty();
+            break;
         case PutById:
         case PutByIdDirect:
         case PutByIdFlush:
@@ -6772,6 +6775,108 @@ private:
         setJSValue(vmCall(Int64, m_out.operation(operationGenericIn), m_callFrame, cell, lowJSValue(m_node->child1())));
     }
 
+    void compileHasOwnProperty()
+    {
+        LBasicBlock slowCase = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+        LBasicBlock lastNext = nullptr;
+
+        LValue object = lowObject(m_node->child1());
+        LValue uniquedStringImpl;
+        LValue keyAsValue = nullptr;
+        switch (m_node->child2().useKind()) {
+        case StringUse: {
+            LBasicBlock isNonEmptyString = m_out.newBlock();
+            LBasicBlock isAtomicString = m_out.newBlock();
+
+            keyAsValue = lowString(m_node->child2());
+            uniquedStringImpl = m_out.loadPtr(keyAsValue, m_heaps.JSString_value);
+            m_out.branch(m_out.notNull(uniquedStringImpl), usually(isNonEmptyString), rarely(slowCase));
+
+            lastNext = m_out.appendTo(isNonEmptyString, isAtomicString);
+            LValue isNotAtomic = m_out.testIsZero32(m_out.load32(uniquedStringImpl, m_heaps.StringImpl_hashAndFlags), m_out.constInt32(StringImpl::flagIsAtomic()));
+            m_out.branch(isNotAtomic, rarely(slowCase), usually(isAtomicString));
+
+            m_out.appendTo(isAtomicString, slowCase);
+            break;
+        }
+        case SymbolUse: {
+            keyAsValue = lowSymbol(m_node->child2());
+            uniquedStringImpl = m_out.loadPtr(keyAsValue, m_heaps.Symbol_symbolImpl);
+            lastNext = m_out.insertNewBlocksBefore(slowCase);
+            break;
+        }
+        case UntypedUse: {
+            LBasicBlock isCellCase = m_out.newBlock();
+            LBasicBlock isStringCase = m_out.newBlock();
+            LBasicBlock notStringCase = m_out.newBlock();
+            LBasicBlock isNonEmptyString = m_out.newBlock();
+            LBasicBlock isSymbolCase = m_out.newBlock();
+            LBasicBlock hasUniquedStringImpl = m_out.newBlock();
+
+            keyAsValue = lowJSValue(m_node->child2());
+            m_out.branch(isCell(keyAsValue), usually(isCellCase), rarely(slowCase));
+
+            lastNext = m_out.appendTo(isCellCase, isStringCase);
+            m_out.branch(isString(keyAsValue), unsure(isStringCase), unsure(notStringCase));
+
+            m_out.appendTo(isStringCase, isNonEmptyString);
+            LValue implFromString = m_out.loadPtr(keyAsValue, m_heaps.JSString_value);
+            ValueFromBlock stringResult = m_out.anchor(implFromString);
+            m_out.branch(m_out.notNull(implFromString), usually(isNonEmptyString), rarely(slowCase));
+
+            m_out.appendTo(isNonEmptyString, notStringCase);
+            LValue isNotAtomic = m_out.testIsZero32(m_out.load32(implFromString, m_heaps.StringImpl_hashAndFlags), m_out.constInt32(StringImpl::flagIsAtomic()));
+            m_out.branch(isNotAtomic, rarely(slowCase), usually(hasUniquedStringImpl));
+
+            m_out.appendTo(notStringCase, isSymbolCase);
+            m_out.branch(isSymbol(keyAsValue), unsure(isSymbolCase), unsure(slowCase));
+
+            m_out.appendTo(isSymbolCase, hasUniquedStringImpl);
+            ValueFromBlock symbolResult = m_out.anchor(m_out.loadPtr(keyAsValue, m_heaps.Symbol_symbolImpl));
+            m_out.jump(hasUniquedStringImpl);
+
+            m_out.appendTo(hasUniquedStringImpl, slowCase);
+            uniquedStringImpl = m_out.phi(pointerType(), stringResult, symbolResult);
+            break;
+        }
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+
+        ASSERT(keyAsValue);
+
+        // Note that we don't test if the hash is zero here. AtomicStringImpl's can't have a zero
+        // hash, however, a SymbolImpl may. But, because this is a cache, we don't care. We only
+        // ever load the result from the cache if the cache entry matches what we are querying for.
+        // So we either get super lucky and use zero for the hash and somehow collide with the entity
+        // we're looking for, or we realize we're comparing against another entity, and go to the
+        // slow path anyways.
+        LValue hash = m_out.lShr(m_out.load32(uniquedStringImpl, m_heaps.StringImpl_hashAndFlags), m_out.constInt32(StringImpl::s_flagCount));
+
+        LValue structureID = m_out.load32(object, m_heaps.JSCell_structureID);
+        LValue index = m_out.add(hash, structureID);
+        index = m_out.zeroExtPtr(m_out.bitAnd(index, m_out.constInt32(HasOwnPropertyCache::mask)));
+        ASSERT(vm().hasOwnPropertyCache());
+        LValue cache = m_out.constIntPtr(vm().hasOwnPropertyCache());
+
+        IndexedAbstractHeap& heap = m_heaps.HasOwnPropertyCache;
+        LValue sameStructureID = m_out.equal(structureID, m_out.load32(m_out.baseIndex(heap, cache, index, JSValue(), HasOwnPropertyCache::Entry::offsetOfStructureID())));
+        LValue sameImpl = m_out.equal(uniquedStringImpl, m_out.loadPtr(m_out.baseIndex(heap, cache, index, JSValue(), HasOwnPropertyCache::Entry::offsetOfImpl())));
+        ValueFromBlock fastResult = m_out.anchor(m_out.load8ZeroExt32(m_out.baseIndex(heap, cache, index, JSValue(), HasOwnPropertyCache::Entry::offsetOfResult())));
+        LValue cacheHit = m_out.bitAnd(sameStructureID, sameImpl);
+
+        m_out.branch(m_out.notZero32(cacheHit), usually(continuation), rarely(slowCase));
+
+        m_out.appendTo(slowCase, continuation);
+        ValueFromBlock slowResult;
+        slowResult = m_out.anchor(vmCall(Int32, m_out.operation(operationHasOwnProperty), m_callFrame, object, keyAsValue));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        setBoolean(m_out.phi(Int32, fastResult, slowResult));
+    }
+
     void compileOverridesHasInstance()
     {
         JSFunction* defaultHasInstanceFunction = jsCast<JSFunction*>(m_node->cellOperand()->value());
index b5bbbe3..26e1ebb 100644 (file)
@@ -29,6 +29,7 @@
 #include "GCActivityCallback.h"
 #include "GCIncomingRefCountedSetInlines.h"
 #include "GCTypeMap.h"
+#include "HasOwnPropertyCache.h"
 #include "HeapHelperPool.h"
 #include "HeapIterationScope.h"
 #include "HeapProfiler.h"
@@ -1022,6 +1023,9 @@ NEVER_INLINE void Heap::collectImpl(HeapOperation collectionType, void* stackOri
         stopAllocation();
         prepareForMarking();
         flushWriteBarrierBuffer();
+
+        if (HasOwnPropertyCache* cache = vm()->hasOwnPropertyCache())
+            cache->clear();
     }
 
     markRoots(gcStartTime, stackOrigin, stackTop, calleeSavedRegisters);
index 882b485..604882f 100644 (file)
@@ -221,6 +221,8 @@ typedef int32_t (JIT_OPERATION *Z_JITOperation_EJ)(ExecState*, EncodedJSValue);
 typedef int32_t (JIT_OPERATION *Z_JITOperation_EJOJ)(ExecState*, EncodedJSValue, JSObject*, EncodedJSValue);
 typedef int32_t (JIT_OPERATION *Z_JITOperation_EJZ)(ExecState*, EncodedJSValue, int32_t);
 typedef int32_t (JIT_OPERATION *Z_JITOperation_EJZZ)(ExecState*, EncodedJSValue, int32_t, int32_t);
+typedef int32_t (JIT_OPERATION *Z_JITOperation_EOI)(ExecState*, JSObject*, UniquedStringImpl*);
+typedef int32_t (JIT_OPERATION *Z_JITOperation_EOJ)(ExecState*, JSObject*, EncodedJSValue);
 typedef size_t (JIT_OPERATION *S_JITOperation_ECC)(ExecState*, JSCell*, JSCell*);
 typedef size_t (JIT_OPERATION *S_JITOperation_EGC)(ExecState*, JSGlobalObject*, JSCell*);
 typedef size_t (JIT_OPERATION *S_JITOperation_EGJJ)(ExecState*, JSGlobalObject*, EncodedJSValue, EncodedJSValue);
diff --git a/Source/JavaScriptCore/runtime/HasOwnPropertyCache.h b/Source/JavaScriptCore/runtime/HasOwnPropertyCache.h
new file mode 100644 (file)
index 0000000..2a26a7a
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#pragma once
+
+#include "JSProxy.h"
+#include "PropertySlot.h"
+#include "Structure.h"
+
+namespace JSC {
+
+class HasOwnPropertyCache {
+    static const uint32_t size = 2 * 1024;
+    static_assert(!(size & (size - 1)), "size should be a power of two.");
+public:
+    static const uint32_t mask = size - 1;
+
+    struct Entry {
+        static ptrdiff_t offsetOfStructureID() { return OBJECT_OFFSETOF(Entry, structureID); }
+        static ptrdiff_t offsetOfImpl() { return OBJECT_OFFSETOF(Entry, impl); }
+        static ptrdiff_t offsetOfResult() { return OBJECT_OFFSETOF(Entry, result); }
+
+        UniquedStringImpl* impl;
+        StructureID structureID;
+        bool result;
+    };
+
+    HasOwnPropertyCache() = delete;
+
+    void operator delete(void* cache)
+    {
+        fastFree(cache);
+    }
+
+    static HasOwnPropertyCache* create()
+    {
+        size_t allocationSize = sizeof(Entry) * size;
+        HasOwnPropertyCache* result = static_cast<HasOwnPropertyCache*>(fastMalloc(allocationSize));
+        result->clear();
+        return result;
+    }
+
+    ALWAYS_INLINE static uint32_t hash(StructureID structureID, UniquedStringImpl* impl)
+    {
+        return bitwise_cast<uint32_t>(structureID) + impl->hash();
+    }
+
+    ALWAYS_INLINE Optional<bool> get(Structure* structure, PropertyName propName)
+    {
+        UniquedStringImpl* impl = propName.uid();
+        StructureID id = structure->id();
+        uint32_t index = HasOwnPropertyCache::hash(id, impl) & mask;
+        Entry& entry = bitwise_cast<Entry*>(this)[index];
+        if (entry.structureID == id && entry.impl == impl)
+            return entry.result;
+        return Nullopt;
+    }
+
+    ALWAYS_INLINE void tryAdd(VM& vm, PropertySlot& slot, JSObject* object, PropertyName propName, bool result)
+    {
+        if (parseIndex(propName))
+            return;
+
+        if (!slot.isCacheable() && !slot.isUnset())
+            return;
+
+        if (object->type() == PureForwardingProxyType || object->type() == ImpureProxyType)
+            return;
+
+        Structure* structure = object->structure(vm);
+        if (!structure->typeInfo().prohibitsPropertyCaching()
+            && structure->propertyAccessesAreCacheable()
+            && (!slot.isUnset() || structure->propertyAccessesAreCacheableForAbsence())) {
+            if (structure->isDictionary()) {
+                if (structure->hasBeenFlattenedBefore())
+                    return;
+                object->flattenDictionaryObject(vm);
+            }
+
+            ASSERT(!result == slot.isUnset());
+
+            UniquedStringImpl* impl = propName.uid();
+            StructureID id = structure->id();
+            uint32_t index = HasOwnPropertyCache::hash(id, impl) & mask;
+            bitwise_cast<Entry*>(this)[index] = Entry{ impl, id, result };
+        }
+    }
+
+    void clear()
+    {
+        memset(this, 0, sizeof(Entry) * size);
+    }
+};
+
+ALWAYS_INLINE HasOwnPropertyCache* VM::ensureHasOwnPropertyCache()
+{
+    if (UNLIKELY(!m_hasOwnPropertyCache))
+        m_hasOwnPropertyCache = std::unique_ptr<HasOwnPropertyCache>(HasOwnPropertyCache::create());
+    return m_hasOwnPropertyCache.get();
+}
+
+} // namespace JSC
index 7f839ef..da713ff 100644 (file)
@@ -65,6 +65,7 @@ enum JS_EXPORT_PRIVATE Intrinsic {
     JSMapGetIntrinsic,
     JSMapHasIntrinsic,
     JSSetHasIntrinsic,
+    HasOwnPropertyIntrinsic,
 
     // Getter intrinsics.
     TypedArrayLengthIntrinsic,
index fba8f11..a928760 100644 (file)
@@ -523,6 +523,7 @@ public:
     JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const;
     bool hasPropertyGeneric(ExecState*, PropertyName, PropertySlot::InternalMethodType) const;
     bool hasPropertyGeneric(ExecState*, unsigned propertyName, PropertySlot::InternalMethodType) const;
+    bool hasOwnProperty(ExecState*, PropertyName, PropertySlot&) const;
     bool hasOwnProperty(ExecState*, PropertyName) const;
     bool hasOwnProperty(ExecState*, unsigned) const;
 
index 5db9398..6c7c321 100644 (file)
@@ -199,14 +199,20 @@ ALWAYS_INLINE bool JSObject::putInline(JSCell* cell, ExecState* exec, PropertyNa
 
 // HasOwnProperty(O, P) from section 7.3.11 in the spec.
 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasownproperty
-ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const
+ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const
 {
-    PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
+    ASSERT(slot.internalMethodType() == PropertySlot::InternalMethodType::GetOwnProperty);
     if (LIKELY(const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlot == JSObject::getOwnPropertySlot))
         return JSObject::getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
     return const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
 }
 
+ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const
+{
+    PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
+    return hasOwnProperty(exec, propertyName, slot);
+}
+
 ALWAYS_INLINE bool JSObject::hasOwnProperty(ExecState* exec, unsigned propertyName) const
 {
     PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
index 02fc760..98a88da 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "Error.h"
 #include "GetterSetter.h"
+#include "HasOwnPropertyCache.h"
 #include "JSFunction.h"
 #include "JSString.h"
 #include "JSCInlines.h"
@@ -60,7 +61,7 @@ void ObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, objectProtoFuncToString, DontEnum, 0);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, objectProtoFuncToLocaleString, DontEnum, 0);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->valueOf, objectProtoFuncValueOf, DontEnum, 0);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->hasOwnProperty, objectProtoFuncHasOwnProperty, DontEnum, 1);
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->hasOwnProperty, objectProtoFuncHasOwnProperty, DontEnum, 1, HasOwnPropertyIntrinsic);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->propertyIsEnumerable, objectProtoFuncPropertyIsEnumerable, DontEnum, 1);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->isPrototypeOf, objectProtoFuncIsPrototypeOf, DontEnum, 1);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->__defineGetter__, objectProtoFuncDefineGetter, DontEnum, 2);
@@ -97,9 +98,24 @@ EncodedJSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState* exec)
     if (UNLIKELY(scope.exception()))
         return JSValue::encode(jsUndefined());
     JSObject* thisObject = thisValue.toObject(exec);
-    if (!thisObject)
+    if (UNLIKELY(!thisObject))
         return JSValue::encode(JSValue());
-    return JSValue::encode(jsBoolean(thisObject->hasOwnProperty(exec, propertyName)));
+
+    Structure* structure = thisObject->structure(vm);
+    HasOwnPropertyCache* hasOwnPropertyCache = vm.ensureHasOwnPropertyCache();
+    if (Optional<bool> result = hasOwnPropertyCache->get(structure, propertyName)) {
+        ASSERT(*result == thisObject->hasOwnProperty(exec, propertyName));
+        ASSERT(!scope.exception());
+        return JSValue::encode(jsBoolean(*result));
+    }
+
+    PropertySlot slot(thisObject, PropertySlot::InternalMethodType::GetOwnProperty);
+    bool result = thisObject->hasOwnProperty(exec, propertyName, slot);
+    if (UNLIKELY(scope.exception()))
+        return JSValue::encode(jsUndefined());
+
+    hasOwnPropertyCache->tryAdd(vm, slot, thisObject, propertyName, result);
+    return JSValue::encode(jsBoolean(result));
 }
 
 EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState* exec)
index 8c70306..0b71c83 100644 (file)
@@ -59,6 +59,12 @@ public:
     JSObject* toObject(ExecState*, JSGlobalObject*) const;
     double toNumber(ExecState*) const;
 
+    static ptrdiff_t offsetOfSymbolImpl()
+    {
+        // PrivateName is just a Ref<SymbolImpl> which can just be used as a SymbolImpl*.
+        return OBJECT_OFFSETOF(Symbol, m_privateName);
+    }
+
 protected:
     static void destroy(JSCell*);
 
index d62e55f..c922d80 100644 (file)
@@ -47,6 +47,7 @@
 #include "FunctionConstructor.h"
 #include "GCActivityCallback.h"
 #include "GetterSetter.h"
+#include "HasOwnPropertyCache.h"
 #include "Heap.h"
 #include "HeapIterationScope.h"
 #include "HeapProfiler.h"
index c9d124c..6526aca 100644 (file)
@@ -89,6 +89,7 @@ class ExceptionScope;
 class HandleStack;
 class TypeProfiler;
 class TypeProfilerLog;
+class HasOwnPropertyCache;
 class HeapProfiler;
 class Identifier;
 class Interpreter;
@@ -554,6 +555,10 @@ public:
     BumpPointerAllocator m_regExpAllocator;
     ConcurrentJITLock m_regExpAllocatorLock;
 
+    std::unique_ptr<HasOwnPropertyCache> m_hasOwnPropertyCache;
+    ALWAYS_INLINE HasOwnPropertyCache* hasOwnPropertyCache() { return m_hasOwnPropertyCache.get(); }
+    HasOwnPropertyCache* ensureHasOwnPropertyCache();
+
 #if ENABLE(REGEXP_TRACING)
     typedef ListHashSet<RegExp*> RTTraceList;
     RTTraceList* m_rtTraceList;