[DFG] InById should be converted to MatchStructure
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 1 Jun 2018 18:39:57 +0000 (18:39 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 1 Jun 2018 18:39:57 +0000 (18:39 +0000)
https://bugs.webkit.org/show_bug.cgi?id=185803

Reviewed by Keith Miller.

JSTests:

* microbenchmarks/in-by-id-match.js: Added.
(test):
* microbenchmarks/in-by-id-removed.js: Added.
(test):
* stress/in-by-id-constant.js: Added.
(shouldBe):
(test1):
* stress/in-by-id-match-invalid.js: Added.
(shouldBe):
(test1):
* stress/in-by-id-match.js: Added.
(shouldBe):
(test1):

Source/JavaScriptCore:

MatchStructure is introduced for instanceof optimization. But this node
is also useful for InById node. This patch converts InById to MatchStructure
node with CheckStructures if possible by using InByIdStatus.

Added microbenchmarks show improvements.

                           baseline                  patched

in-by-id-removed       18.1196+-0.8108     ^     16.1702+-0.9773        ^ definitely 1.1206x faster
in-by-id-match         16.3912+-0.2608     ^     15.2736+-0.8173        ^ definitely 1.0732x faster

* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* bytecode/InByIdStatus.cpp: Added.
(JSC::InByIdStatus::appendVariant):
(JSC::InByIdStatus::computeFor):
(JSC::InByIdStatus::hasExitSite):
(JSC::InByIdStatus::computeForStubInfo):
(JSC::InByIdStatus::computeForStubInfoWithoutExitSiteFeedback):
(JSC::InByIdStatus::filter):
(JSC::InByIdStatus::dump const):
* bytecode/InByIdStatus.h: Added.
(JSC::InByIdStatus::InByIdStatus):
(JSC::InByIdStatus::state const):
(JSC::InByIdStatus::isSet const):
(JSC::InByIdStatus::operator bool const):
(JSC::InByIdStatus::isSimple const):
(JSC::InByIdStatus::numVariants const):
(JSC::InByIdStatus::variants const):
(JSC::InByIdStatus::at const):
(JSC::InByIdStatus::operator[] const):
(JSC::InByIdStatus::takesSlowPath const):
* bytecode/InByIdVariant.cpp: Added.
(JSC::InByIdVariant::InByIdVariant):
(JSC::InByIdVariant::attemptToMerge):
(JSC::InByIdVariant::dump const):
(JSC::InByIdVariant::dumpInContext const):
* bytecode/InByIdVariant.h: Added.
(JSC::InByIdVariant::isSet const):
(JSC::InByIdVariant::operator bool const):
(JSC::InByIdVariant::structureSet const):
(JSC::InByIdVariant::structureSet):
(JSC::InByIdVariant::conditionSet const):
(JSC::InByIdVariant::offset const):
(JSC::InByIdVariant::isHit const):
* bytecode/PolyProtoAccessChain.h:
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):

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

15 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/in-by-id-match.js [new file with mode: 0644]
JSTests/microbenchmarks/in-by-id-removed.js [new file with mode: 0644]
JSTests/stress/in-by-id-constant.js [new file with mode: 0644]
JSTests/stress/in-by-id-match-invalid.js [new file with mode: 0644]
JSTests/stress/in-by-id-match.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Sources.txt
Source/JavaScriptCore/bytecode/InByIdStatus.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/InByIdStatus.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/InByIdVariant.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/InByIdVariant.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/PolyProtoAccessChain.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

index c6f6f12..9e513dc 100644 (file)
@@ -1,3 +1,24 @@
+2018-05-30  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DFG] InById should be converted to MatchStructure
+        https://bugs.webkit.org/show_bug.cgi?id=185803
+
+        Reviewed by Keith Miller.
+
+        * microbenchmarks/in-by-id-match.js: Added.
+        (test):
+        * microbenchmarks/in-by-id-removed.js: Added.
+        (test):
+        * stress/in-by-id-constant.js: Added.
+        (shouldBe):
+        (test1):
+        * stress/in-by-id-match-invalid.js: Added.
+        (shouldBe):
+        (test1):
+        * stress/in-by-id-match.js: Added.
+        (shouldBe):
+        (test1):
+
 2018-05-31  Caio Lima  <ticaiolima@gmail.com>
 
         [ESNext][BigInt] Implement support for "=<" and ">=" relational operation
diff --git a/JSTests/microbenchmarks/in-by-id-match.js b/JSTests/microbenchmarks/in-by-id-match.js
new file mode 100644 (file)
index 0000000..d287c86
--- /dev/null
@@ -0,0 +1,19 @@
+function test(object)
+{
+    if ("cocoa" in object)
+        return object.cocoa;
+    return 0;
+}
+noInline(test);
+
+var o1 = {
+    cocoa: 42
+};
+var o2 = {
+    cappuccino: 41
+};
+
+for (var i = 0; i < 1e6; ++i) {
+    test(o1);
+    test(o2);
+}
diff --git a/JSTests/microbenchmarks/in-by-id-removed.js b/JSTests/microbenchmarks/in-by-id-removed.js
new file mode 100644 (file)
index 0000000..48a8278
--- /dev/null
@@ -0,0 +1,20 @@
+function test(object)
+{
+    if ("cocoa" in object)
+        return object.cocoa;
+    return 0;
+}
+noInline(test);
+
+var o1 = {
+    cocoa: 42
+};
+var o2 = {
+    cocoa: 40,
+    cappuccino: 41
+};
+
+for (var i = 0; i < 1e6; ++i) {
+    test(o1);
+    test(o2);
+}
diff --git a/JSTests/stress/in-by-id-constant.js b/JSTests/stress/in-by-id-constant.js
new file mode 100644 (file)
index 0000000..9319ea8
--- /dev/null
@@ -0,0 +1,33 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test1(obj)
+{
+    return "hello" in obj
+}
+noInline(test1);
+
+let array = [
+    [{
+        hello: 42
+    }, true],
+    [{
+        hello: 42,
+        world: 43
+    }, true],
+    [{
+        __proto__: {
+            hello: 44
+        }
+    }, true]
+];
+for (let i = 0; i < 1e5; ++i) {
+    for (let [obj, result] of array)
+        shouldBe(test1(obj), result);
+}
+
+// OSRExits.
+for (let i = 0; i < 1e5; ++i)
+    shouldBe(test1({}), false);
diff --git a/JSTests/stress/in-by-id-match-invalid.js b/JSTests/stress/in-by-id-match-invalid.js
new file mode 100644 (file)
index 0000000..51755e2
--- /dev/null
@@ -0,0 +1,30 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test1(obj)
+{
+    return "hello" in obj
+}
+noInline(test1);
+
+let object1 = {
+    hello: 42
+};
+let object2 = {
+    cocoa: 44
+};
+for (let i = 0; i < 1e5; ++i) {
+    shouldBe(test1(object1), true);
+    shouldBe(test1(object2), false);
+}
+object2.hello = 44;
+for (let i = 0; i < 1e5; ++i) {
+    shouldBe(test1(object1), true);
+    shouldBe(test1(object2), true);
+}
+
+// OSRExits.
+for (let i = 0; i < 1e5; ++i)
+    shouldBe(test1({}), false);
diff --git a/JSTests/stress/in-by-id-match.js b/JSTests/stress/in-by-id-match.js
new file mode 100644 (file)
index 0000000..62ccc31
--- /dev/null
@@ -0,0 +1,32 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test1(obj)
+{
+    return "hello" in obj
+}
+noInline(test1);
+
+let array = [
+    [{
+        hello: 42
+    }, true],
+    [{
+        world: 43
+    }, false],
+    [{
+        __proto__: {
+            hello: 44
+        }
+    }, true]
+];
+for (let i = 0; i < 1e5; ++i) {
+    for (let [obj, result] of array)
+        shouldBe(test1(obj), result);
+}
+
+// OSRExits.
+for (let i = 0; i < 1e5; ++i)
+    shouldBe(test1({}), false);
index b881334..3d25986 100644 (file)
@@ -1,3 +1,59 @@
+2018-05-30  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DFG] InById should be converted to MatchStructure
+        https://bugs.webkit.org/show_bug.cgi?id=185803
+
+        Reviewed by Keith Miller.
+
+        MatchStructure is introduced for instanceof optimization. But this node
+        is also useful for InById node. This patch converts InById to MatchStructure
+        node with CheckStructures if possible by using InByIdStatus.
+
+        Added microbenchmarks show improvements.
+
+                                   baseline                  patched
+
+        in-by-id-removed       18.1196+-0.8108     ^     16.1702+-0.9773        ^ definitely 1.1206x faster
+        in-by-id-match         16.3912+-0.2608     ^     15.2736+-0.8173        ^ definitely 1.0732x faster
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * Sources.txt:
+        * bytecode/InByIdStatus.cpp: Added.
+        (JSC::InByIdStatus::appendVariant):
+        (JSC::InByIdStatus::computeFor):
+        (JSC::InByIdStatus::hasExitSite):
+        (JSC::InByIdStatus::computeForStubInfo):
+        (JSC::InByIdStatus::computeForStubInfoWithoutExitSiteFeedback):
+        (JSC::InByIdStatus::filter):
+        (JSC::InByIdStatus::dump const):
+        * bytecode/InByIdStatus.h: Added.
+        (JSC::InByIdStatus::InByIdStatus):
+        (JSC::InByIdStatus::state const):
+        (JSC::InByIdStatus::isSet const):
+        (JSC::InByIdStatus::operator bool const):
+        (JSC::InByIdStatus::isSimple const):
+        (JSC::InByIdStatus::numVariants const):
+        (JSC::InByIdStatus::variants const):
+        (JSC::InByIdStatus::at const):
+        (JSC::InByIdStatus::operator[] const):
+        (JSC::InByIdStatus::takesSlowPath const):
+        * bytecode/InByIdVariant.cpp: Added.
+        (JSC::InByIdVariant::InByIdVariant):
+        (JSC::InByIdVariant::attemptToMerge):
+        (JSC::InByIdVariant::dump const):
+        (JSC::InByIdVariant::dumpInContext const):
+        * bytecode/InByIdVariant.h: Added.
+        (JSC::InByIdVariant::isSet const):
+        (JSC::InByIdVariant::operator bool const):
+        (JSC::InByIdVariant::structureSet const):
+        (JSC::InByIdVariant::structureSet):
+        (JSC::InByIdVariant::conditionSet const):
+        (JSC::InByIdVariant::offset const):
+        (JSC::InByIdVariant::isHit const):
+        * bytecode/PolyProtoAccessChain.h:
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+
 2018-06-01  Keith Miller  <keith_miller@apple.com>
 
         move should only emit the move if it's actually needed
index 29cbab4..13dafbb 100644 (file)
                E328DAE91D38D005001A2529 /* BytecodeGraph.h in Headers */ = {isa = PBXBuildFile; fileRef = E3D264281D38C042000BE174 /* BytecodeGraph.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E328DAEB1D38D005001A2529 /* BytecodeRewriter.h in Headers */ = {isa = PBXBuildFile; fileRef = E3D2642A1D38C042000BE174 /* BytecodeRewriter.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E32AB2441DCD75F400D7533A /* MacroAssemblerHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = E380A76B1DCD7195000F89E6 /* MacroAssemblerHelpers.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E3305FB320B0F78800CEB82B /* InByIdStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = E3305FAF20B0F78700CEB82B /* InByIdStatus.h */; };
+               E3305FB520B0F78800CEB82B /* InByIdVariant.h in Headers */ = {isa = PBXBuildFile; fileRef = E3305FB120B0F78800CEB82B /* InByIdVariant.h */; };
                E33637A61B63220200EE0840 /* ReflectObject.h in Headers */ = {isa = PBXBuildFile; fileRef = E33637A41B63220200EE0840 /* ReflectObject.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E33B3E261B7ABD750048DB2E /* InspectorInstrumentationObject.lut.h in Headers */ = {isa = PBXBuildFile; fileRef = E33B3E251B7ABD750048DB2E /* InspectorInstrumentationObject.lut.h */; };
                E33E8D1D1B9013C300346B52 /* JSNativeStdFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = E33E8D1B1B9013C300346B52 /* JSNativeStdFunction.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E326C4961ECBEF5700A9A905 /* ClassInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClassInfo.cpp; sourceTree = "<group>"; };
                E3282BB91FE930A300EDAF71 /* YarrErrorCode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = YarrErrorCode.cpp; path = yarr/YarrErrorCode.cpp; sourceTree = "<group>"; };
                E3282BBA1FE930A400EDAF71 /* YarrErrorCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YarrErrorCode.h; path = yarr/YarrErrorCode.h; sourceTree = "<group>"; };
+               E3305FAF20B0F78700CEB82B /* InByIdStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InByIdStatus.h; sourceTree = "<group>"; };
+               E3305FB020B0F78700CEB82B /* InByIdVariant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InByIdVariant.cpp; sourceTree = "<group>"; };
+               E3305FB120B0F78800CEB82B /* InByIdVariant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InByIdVariant.h; sourceTree = "<group>"; };
+               E3305FB220B0F78800CEB82B /* InByIdStatus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InByIdStatus.cpp; sourceTree = "<group>"; };
                E33637A31B63220200EE0840 /* ReflectObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReflectObject.cpp; sourceTree = "<group>"; };
                E33637A41B63220200EE0840 /* ReflectObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReflectObject.h; sourceTree = "<group>"; };
                E33B3E251B7ABD750048DB2E /* InspectorInstrumentationObject.lut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorInstrumentationObject.lut.h; sourceTree = "<group>"; };
                                14BD59BF0A3E8F9000BAF59C /* testapi */,
                                0FEC85AD1BDB5CF10080FF74 /* testb3 */,
                                FE533CAC1F217DB40016A1FE /* testmasm */,
+                               79281BDC20B62B3E002E2A60 /* testmem */,
                                6511230514046A4C002B101D /* testRegExp */,
                                932F5BD90822A1C700736975 /* JavaScriptCore.framework */,
-                               79281BDC20B62B3E002E2A60 /* testmem */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                79ABCC5520B7812600323D5F /* testmem2 */ = {
                        isa = PBXGroup;
                        children = (
-                               79ABCC5620B7812600323D5F /* testmem2.m */,
                                79ABCC5820B7812600323D5F /* testmem2.1 */,
+                               79ABCC5620B7812600323D5F /* testmem2.m */,
                        );
                        path = testmem2;
                        sourceTree = "<group>";
                                14AD91081DCA92940014F9FE /* GlobalCodeBlock.h */,
                                0F0B83A814BCF55E00885B4F /* HandlerInfo.h */,
                                0FB399BD20AF6B380017E213 /* ICStatusUtils.h */,
+                               E3305FB220B0F78800CEB82B /* InByIdStatus.cpp */,
+                               E3305FAF20B0F78700CEB82B /* InByIdStatus.h */,
+                               E3305FB020B0F78700CEB82B /* InByIdVariant.cpp */,
+                               E3305FB120B0F78800CEB82B /* InByIdVariant.h */,
                                7905BB661D12050E0019FE57 /* InlineAccess.cpp */,
                                7905BB671D12050E0019FE57 /* InlineAccess.h */,
                                148A7BED1B82975A002D9157 /* InlineCallFrame.cpp */,
                                0F4C91661C29F4F2004341A6 /* B3OriginDump.h in Headers */,
                                0FEC85261BDACDAC0080FF74 /* B3PatchpointSpecial.h in Headers */,
                                0FEC85281BDACDAC0080FF74 /* B3PatchpointValue.h in Headers */,
-                               0FD2FD9520B52BE200F09441 /* IsoSubspaceInlines.h in Headers */,
                                799EF7C41C56ED96002B0534 /* B3PCToOriginMap.h in Headers */,
                                0FEC852A1BDACDAC0080FF74 /* B3PhaseScope.h in Headers */,
                                0F37308D1C0BD29100052BFA /* B3PhiChildren.h in Headers */,
                                A7E5A3A81797432D00E893C0 /* CompilationResult.h in Headers */,
                                0F4F11E8209BCDAB00709654 /* CompilerTimingScope.h in Headers */,
                                0FDCE12A1FAFA85F006F3901 /* CompleteSubspace.h in Headers */,
+                               0FD2FD9420B52BDE00F09441 /* CompleteSubspaceInlines.h in Headers */,
                                BC18C3F40E16F5CD00B34460 /* Completion.h in Headers */,
                                0F6FC751196110A800E1D02D /* ComplexGetStatus.h in Headers */,
                                0FDB2CEA174896C7007B3C1B /* ConcurrentJSLock.h in Headers */,
                                0FB467801FDDA6F1003FCB09 /* IsoCellSet.h in Headers */,
                                0FB467811FDDA6F7003FCB09 /* IsoCellSetInlines.h in Headers */,
                                0FDCE12D1FAFB4E5006F3901 /* IsoSubspace.h in Headers */,
+                               0FD2FD9520B52BE200F09441 /* IsoSubspaceInlines.h in Headers */,
                                0F5E0FE72086AD480097F0DE /* IsoSubspacePerVM.h in Headers */,
                                8B9F6D561D5912FA001C739F /* IterationKind.h in Headers */,
                                FE4D55B81AE716CA0052E459 /* IterationStatus.h in Headers */,
                                BC18C4140E16F5CD00B34460 /* JavaScriptCore.h in Headers */,
                                BC18C4150E16F5CD00B34460 /* JavaScriptCorePrefix.h in Headers */,
                                1429D9300ED22D7000B89619 /* JIT.h in Headers */,
-                               0FD2FD9420B52BDE00F09441 /* CompleteSubspaceInlines.h in Headers */,
                                FE1220271BE7F58C0039E6F2 /* JITAddGenerator.h in Headers */,
                                0F75A0662013E4F10038E2CF /* JITAllocator.h in Headers */,
                                FE3A06B21C10CB8900390FDD /* JITBitAndGenerator.h in Headers */,
index c011ee6..1da1f73 100644 (file)
@@ -219,6 +219,8 @@ bytecode/FunctionCodeBlock.cpp
 bytecode/GetByIdStatus.cpp
 bytecode/GetByIdVariant.cpp
 bytecode/GetterSetterAccessCase.cpp
+bytecode/InByIdStatus.cpp
+bytecode/InByIdVariant.cpp
 bytecode/InlineAccess.cpp
 bytecode/InlineCallFrame.cpp
 bytecode/InlineCallFrameSet.cpp
diff --git a/Source/JavaScriptCore/bytecode/InByIdStatus.cpp b/Source/JavaScriptCore/bytecode/InByIdStatus.cpp
new file mode 100644 (file)
index 0000000..8b310fb
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2018 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * 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.
+ */
+
+#include "config.h"
+#include "InByIdStatus.h"
+
+#include "CodeBlock.h"
+#include "ComplexGetStatus.h"
+#include "ICStatusUtils.h"
+#include "JSCInlines.h"
+#include "PolymorphicAccess.h"
+#include "StructureStubInfo.h"
+#include <wtf/ListDump.h>
+
+namespace JSC {
+
+bool InByIdStatus::appendVariant(const InByIdVariant& variant)
+{
+    return appendICStatusVariant(m_variants, variant);
+}
+
+InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid)
+{
+    ConcurrentJSLocker locker(profiledBlock->m_lock);
+
+    InByIdStatus result;
+
+#if ENABLE(DFG_JIT)
+    result = computeForStubInfoWithoutExitSiteFeedback(locker, map.get(CodeOrigin(bytecodeIndex)), uid);
+
+    if (!result.takesSlowPath() && hasExitSite(profiledBlock, bytecodeIndex))
+        return InByIdStatus(TakesSlowPath);
+#else
+    UNUSED_PARAM(map);
+#endif
+
+    return result;
+}
+
+InByIdStatus InByIdStatus::computeFor(
+    CodeBlock* profiledBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap,
+    StubInfoMap& dfgMap, CodeOrigin codeOrigin, UniquedStringImpl* uid)
+{
+#if ENABLE(DFG_JIT)
+    if (dfgBlock) {
+        CallLinkStatus::ExitSiteData exitSiteData;
+        {
+            ConcurrentJSLocker locker(profiledBlock->m_lock);
+            exitSiteData = CallLinkStatus::computeExitSiteData(
+                profiledBlock, codeOrigin.bytecodeIndex);
+        }
+
+        InByIdStatus result;
+        {
+            ConcurrentJSLocker locker(dfgBlock->m_lock);
+            result = computeForStubInfoWithoutExitSiteFeedback(locker, dfgMap.get(codeOrigin), uid);
+        }
+
+        if (result.takesSlowPath())
+            return result;
+
+        if (hasExitSite(profiledBlock, codeOrigin.bytecodeIndex))
+            return InByIdStatus(TakesSlowPath);
+
+        if (result.isSet())
+            return result;
+    }
+#else
+    UNUSED_PARAM(dfgBlock);
+    UNUSED_PARAM(dfgMap);
+#endif
+
+    return computeFor(profiledBlock, baselineMap, codeOrigin.bytecodeIndex, uid);
+}
+
+#if ENABLE(DFG_JIT)
+bool InByIdStatus::hasExitSite(CodeBlock* profiledBlock, unsigned bytecodeIndex)
+{
+    UnlinkedCodeBlock* unlinkedCodeBlock = profiledBlock->unlinkedCodeBlock();
+    ConcurrentJSLocker locker(unlinkedCodeBlock->m_lock);
+    return unlinkedCodeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCache))
+        || unlinkedCodeBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadConstantCache));
+}
+
+InByIdStatus InByIdStatus::computeForStubInfo(const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, CodeOrigin codeOrigin, UniquedStringImpl* uid)
+{
+    InByIdStatus result = InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(locker, stubInfo, uid);
+
+    if (!result.takesSlowPath() && InByIdStatus::hasExitSite(profiledBlock, codeOrigin.bytecodeIndex))
+        return InByIdStatus(TakesSlowPath);
+    return result;
+}
+
+InByIdStatus InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(const ConcurrentJSLocker&, StructureStubInfo* stubInfo, UniquedStringImpl* uid)
+{
+    if (!stubInfo || !stubInfo->everConsidered)
+        return InByIdStatus(NoInformation);
+
+    if (stubInfo->tookSlowPath)
+        return InByIdStatus(TakesSlowPath);
+
+    // Finally figure out if we can derive an access strategy.
+    InByIdStatus result;
+    result.m_state = Simple;
+    switch (stubInfo->cacheType) {
+    case CacheType::Unset:
+        return InByIdStatus(NoInformation);
+
+    case CacheType::InByIdSelf: {
+        Structure* structure = stubInfo->u.byIdSelf.baseObjectStructure.get();
+        if (structure->takesSlowPathInDFGForImpureProperty())
+            return InByIdStatus(TakesSlowPath);
+        unsigned attributes;
+        InByIdVariant variant;
+        variant.m_offset = structure->getConcurrently(uid, attributes);
+        if (!isValidOffset(variant.m_offset))
+            return InByIdStatus(TakesSlowPath);
+        if (attributes & PropertyAttribute::CustomAccessor)
+            return InByIdStatus(TakesSlowPath);
+
+        variant.m_structureSet.add(structure);
+        bool didAppend = result.appendVariant(variant);
+        ASSERT_UNUSED(didAppend, didAppend);
+        return result;
+    }
+
+    case CacheType::Stub: {
+        PolymorphicAccess* list = stubInfo->u.stub;
+        for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) {
+            const AccessCase& access = list->at(listIndex);
+            if (access.viaProxy())
+                return InByIdStatus(TakesSlowPath);
+
+            if (access.usesPolyProto())
+                return InByIdStatus(TakesSlowPath);
+
+            Structure* structure = access.structure();
+            if (!structure) {
+                // The null structure cases arise due to array.length. We have no way of creating a
+                // InByIdVariant for those, and we don't really have to since the DFG handles those
+                // cases in FixupPhase using value profiling. That's a bit awkward - we shouldn't
+                // have to use value profiling to discover something that the AccessCase could have
+                // told us. But, it works well enough. So, our only concern here is to not
+                // crash on null structure.
+                return InByIdStatus(TakesSlowPath);
+            }
+
+            ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor(structure, access.conditionSet(), uid);
+            switch (complexGetStatus.kind()) {
+            case ComplexGetStatus::ShouldSkip:
+                continue;
+
+            case ComplexGetStatus::TakesSlowPath:
+                return InByIdStatus(TakesSlowPath);
+
+            case ComplexGetStatus::Inlineable: {
+                switch (access.type()) {
+                case AccessCase::InHit:
+                case AccessCase::InMiss:
+                    break;
+                default:
+                    return InByIdStatus(TakesSlowPath);
+                }
+
+                InByIdVariant variant(
+                    StructureSet(structure), complexGetStatus.offset(),
+                    complexGetStatus.conditionSet());
+
+                if (!result.appendVariant(variant))
+                    return InByIdStatus(TakesSlowPath);
+                break;
+            }
+            }
+        }
+
+        return result;
+    }
+
+    default:
+        return InByIdStatus(TakesSlowPath);
+    }
+
+    RELEASE_ASSERT_NOT_REACHED();
+    return InByIdStatus();
+}
+#endif
+
+void InByIdStatus::filter(const StructureSet& structureSet)
+{
+    if (m_state != Simple)
+        return;
+    filterICStatusVariants(m_variants, structureSet);
+    if (m_variants.isEmpty())
+        m_state = NoInformation;
+}
+
+void InByIdStatus::dump(PrintStream& out) const
+{
+    out.print("(");
+    switch (m_state) {
+    case NoInformation:
+        out.print("NoInformation");
+        break;
+    case Simple:
+        out.print("Simple");
+        break;
+    case TakesSlowPath:
+        out.print("TakesSlowPath");
+        break;
+    }
+    out.print(", ", listDump(m_variants), ")");
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/InByIdStatus.h b/Source/JavaScriptCore/bytecode/InByIdStatus.h
new file mode 100644 (file)
index 0000000..6615dfc
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * 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 "CallLinkStatus.h"
+#include "CodeOrigin.h"
+#include "ConcurrentJSLock.h"
+#include "InByIdVariant.h"
+
+namespace JSC {
+
+class AccessCase;
+class CodeBlock;
+class StructureStubInfo;
+
+typedef HashMap<CodeOrigin, StructureStubInfo*, CodeOriginApproximateHash> StubInfoMap;
+
+class InByIdStatus {
+public:
+    enum State {
+        // It's uncached so we have no information.
+        NoInformation,
+        // It's cached for a simple access to a known object property with
+        // a possible structure chain and a possible specific value.
+        Simple,
+        // It's known to often take slow path.
+        TakesSlowPath,
+    };
+
+    InByIdStatus() = default;
+
+    InByIdStatus(State state, const InByIdVariant& variant = InByIdVariant())
+        : m_state(state)
+    {
+        ASSERT((state == Simple) == variant.isSet());
+        m_variants.append(variant);
+    }
+
+    static InByIdStatus computeFor(CodeBlock*, StubInfoMap&, unsigned bytecodeIndex, UniquedStringImpl* uid);
+    static InByIdStatus computeFor(CodeBlock* baselineBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap, StubInfoMap& dfgMap, CodeOrigin, UniquedStringImpl* uid);
+
+#if ENABLE(DFG_JIT)
+    static InByIdStatus computeForStubInfo(const ConcurrentJSLocker&, CodeBlock* baselineBlock, StructureStubInfo*, CodeOrigin, UniquedStringImpl* uid);
+#endif
+
+    State state() const { return m_state; }
+
+    bool isSet() const { return m_state != NoInformation; }
+    explicit operator bool() const { return isSet(); }
+    bool isSimple() const { return m_state == Simple; }
+
+    size_t numVariants() const { return m_variants.size(); }
+    const Vector<InByIdVariant, 1>& variants() const { return m_variants; }
+    const InByIdVariant& at(size_t index) const { return m_variants[index]; }
+    const InByIdVariant& operator[](size_t index) const { return at(index); }
+
+    bool takesSlowPath() const { return m_state == TakesSlowPath; }
+
+    // Attempts to reduce the set of variants to fit the given structure set. This may be approximate.
+    void filter(const StructureSet&);
+
+    void dump(PrintStream&) const;
+
+private:
+#if ENABLE(DFG_JIT)
+    static bool hasExitSite(CodeBlock*, unsigned bytecodeIndex);
+    static InByIdStatus computeForStubInfoWithoutExitSiteFeedback(const ConcurrentJSLocker&, StructureStubInfo*, UniquedStringImpl* uid);
+#endif
+    bool appendVariant(const InByIdVariant&);
+
+    State m_state { NoInformation };
+    Vector<InByIdVariant, 1> m_variants;
+};
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/bytecode/InByIdVariant.cpp b/Source/JavaScriptCore/bytecode/InByIdVariant.cpp
new file mode 100644 (file)
index 0000000..626a5d3
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * 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.
+ */
+
+#include "config.h"
+#include "InByIdVariant.h"
+
+#include "JSCInlines.h"
+#include <wtf/ListDump.h>
+
+namespace JSC {
+
+InByIdVariant::InByIdVariant(const StructureSet& structureSet, PropertyOffset offset, const ObjectPropertyConditionSet& conditionSet)
+    : m_structureSet(structureSet)
+    , m_conditionSet(conditionSet)
+    , m_offset(offset)
+{
+    if (!structureSet.size()) {
+        ASSERT(offset == invalidOffset);
+        ASSERT(conditionSet.isEmpty());
+    }
+}
+
+bool InByIdVariant::attemptToMerge(const InByIdVariant& other)
+{
+    if (m_offset != other.m_offset)
+        return false;
+
+    if (m_conditionSet.isEmpty() != other.m_conditionSet.isEmpty())
+        return false;
+
+    ObjectPropertyConditionSet mergedConditionSet;
+    if (!m_conditionSet.isEmpty()) {
+        mergedConditionSet = m_conditionSet.mergedWith(other.m_conditionSet);
+        if (!mergedConditionSet.isValid() || !mergedConditionSet.hasOneSlotBaseCondition())
+            return false;
+    }
+    m_conditionSet = mergedConditionSet;
+
+    m_structureSet.merge(other.m_structureSet);
+
+    return true;
+}
+
+void InByIdVariant::dump(PrintStream& out) const
+{
+    dumpInContext(out, 0);
+}
+
+void InByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const
+{
+    if (!isSet()) {
+        out.print("<empty>");
+        return;
+    }
+
+    out.print(
+        "<", inContext(structureSet(), context), ", ", inContext(m_conditionSet, context));
+    out.print(", offset = ", offset());
+    out.print(">");
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/InByIdVariant.h b/Source/JavaScriptCore/bytecode/InByIdVariant.h
new file mode 100644 (file)
index 0000000..b7898a3
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * 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 "ObjectPropertyConditionSet.h"
+#include "PropertyOffset.h"
+#include "StructureSet.h"
+
+namespace JSC {
+namespace DOMJIT {
+class GetterSetter;
+}
+
+class InByIdStatus;
+struct DumpContext;
+
+class InByIdVariant {
+public:
+    InByIdVariant(const StructureSet& = StructureSet(), PropertyOffset = invalidOffset, const ObjectPropertyConditionSet& = ObjectPropertyConditionSet());
+
+    bool isSet() const { return !!m_structureSet.size(); }
+    explicit operator bool() const { return isSet(); }
+    const StructureSet& structureSet() const { return m_structureSet; }
+    StructureSet& structureSet() { return m_structureSet; }
+
+    // A non-empty condition set means that this is a prototype in-hit/in-miss.
+    const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; }
+
+    PropertyOffset offset() const { return m_offset; }
+
+    bool isHit() const { return offset() != invalidOffset; }
+
+    bool attemptToMerge(const InByIdVariant& other);
+
+    void dump(PrintStream&) const;
+    void dumpInContext(PrintStream&, DumpContext*) const;
+
+private:
+    friend class InByIdStatus;
+
+    StructureSet m_structureSet;
+    ObjectPropertyConditionSet m_conditionSet;
+    PropertyOffset m_offset;
+};
+
+} // namespace JSC
index 52ab76e..b49e4e2 100644 (file)
@@ -29,6 +29,7 @@
 
 namespace JSC {
 
+class JSCell;
 class JSGlobalObject;
 class JSObject;
 class PropertySlot;
index bbac6cc..d9bd850 100644 (file)
@@ -48,6 +48,7 @@
 #include "FunctionCodeBlock.h"
 #include "GetByIdStatus.h"
 #include "Heap.h"
+#include "InByIdStatus.h"
 #include "InstanceOfStatus.h"
 #include "JSCInlines.h"
 #include "JSFixedArray.h"
@@ -4805,21 +4806,27 @@ void ByteCodeParser::parseBlock(unsigned limit)
             if (JSObject* commonPrototype = status.commonPrototype()) {
                 addToGraph(CheckCell, OpInfo(m_graph.freeze(commonPrototype)), prototype);
                 
+                bool allOK = true;
                 MatchStructureData* data = m_graph.m_matchStructureData.add();
                 for (const InstanceOfVariant& variant : status.variants()) {
-                    check(variant.conditionSet());
+                    if (!check(variant.conditionSet())) {
+                        allOK = false;
+                        break;
+                    }
                     for (Structure* structure : variant.structureSet()) {
                         MatchStructureVariant matchVariant;
                         matchVariant.structure = m_graph.registerStructure(structure);
                         matchVariant.result = variant.isHit();
                         
-                        data->variants.append(matchVariant);
+                        data->variants.append(WTFMove(matchVariant));
                     }
                 }
                 
-                Node* match = addToGraph(MatchStructure, OpInfo(data), value);
-                set(VirtualRegister(bytecode.dst()), match);
-                NEXT_OPCODE(op_instanceof);
+                if (allOK) {
+                    Node* match = addToGraph(MatchStructure, OpInfo(data), value);
+                    set(VirtualRegister(bytecode.dst()), match);
+                    NEXT_OPCODE(op_instanceof);
+                }
             }
             
             set(VirtualRegister(bytecode.dst()), addToGraph(InstanceOf, value, prototype));
@@ -6423,10 +6430,39 @@ void ByteCodeParser::parseBlock(unsigned limit)
         case op_in_by_id: {
             Node* base = get(VirtualRegister(currentInstruction[2].u.operand));
             unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[currentInstruction[3].u.operand];
-            set(VirtualRegister(currentInstruction[1].u.operand),
-                addToGraph(InById, OpInfo(identifierNumber), base));
+            UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber];
+
+            InByIdStatus status = InByIdStatus::computeFor(
+                m_inlineStackTop->m_profiledBlock, m_dfgCodeBlock,
+                m_inlineStackTop->m_stubInfos, m_dfgStubInfos,
+                currentCodeOrigin(), uid);
+
+            if (status.isSimple()) {
+                bool allOK = true;
+                MatchStructureData* data = m_graph.m_matchStructureData.add();
+                for (const InByIdVariant& variant : status.variants()) {
+                    if (!check(variant.conditionSet())) {
+                        allOK = false;
+                        break;
+                    }
+                    for (Structure* structure : variant.structureSet()) {
+                        MatchStructureVariant matchVariant;
+                        matchVariant.structure = m_graph.registerStructure(structure);
+                        matchVariant.result = variant.isHit();
+
+                        data->variants.append(WTFMove(matchVariant));
+                    }
+                }
+
+                if (allOK) {
+                    Node* match = addToGraph(MatchStructure, OpInfo(data), base);
+                    set(VirtualRegister(currentInstruction[1].u.operand), match);
+                    NEXT_OPCODE(op_in_by_id);
+                }
+            }
+
+            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(InById, OpInfo(identifierNumber), base));
             NEXT_OPCODE(op_in_by_id);
-            break;
         }
 
         case op_get_enumerable_length: {