JSModuleNamespace object should have IC
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Feb 2017 09:23:08 +0000 (09:23 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Feb 2017 09:23:08 +0000 (09:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=160590

Reviewed by Saam Barati.

JSTests:

* modules/module-assert-access-binding.js: Added.
* modules/module-assert-access-namespace.js: Added.
* modules/namespace-empty.js: Added.
(from.string_appeared_here.access):
(i.shouldThrow):
* stress/module-namespace-access-change.js: Added.
(shouldBe):
(access):
(import.string_appeared_here.then):
* stress/module-namespace-access-non-constant.js: Added.
(shouldBe):
(import.string_appeared_here.then):
* stress/module-namespace-access-poly.js: Added.
(shouldBe):
(access):
(import.string_appeared_here.then):
* stress/module-namespace-access-transitive-exports.js: Added.
(shouldBe):
(import.string_appeared_here.then):
* stress/module-namespace-access.js: Added.
(shouldBe):
(import.string_appeared_here.then):
* stress/resources/module-namespace-access-transitive-exports-2.js: Added.
(export.cocoa):
(export.change):
* stress/resources/module-namespace-access-transitive-exports.js: Added.
* stress/resources/module-namespace-access.js: Added.
(export.cocoa):
(export.change):

Source/JavaScriptCore:

This patch optimizes accesses to module namespace objects.

1. Cache the resolutions for module namespace objects.

    When constructing the module namespace object, we already resolves all the exports.
    The module namespace object caches this result and leverage it in the later access in
    getOwnPropertySlot. This avoids resolving bindings through resolveExport.

2. Introduce ModuleNamespaceLoad IC.

    This patch adds new IC for module namespace objects. The mechanism is simple, getOwnPropertySlot
    tells us about module namespace object resolution. The IC first checks whether the given object
    is an expected module namespace object. If this check succeeds, we load the value from the module
    environment.

3. Introduce DFG/FTL optimization.

    After exploiting module namespace object accesses in (2), DFG can recognize this in ByteCodeParser.
    DFG will convert it to CheckCell with the namespace object and GetClosureVar from the cached environment.
    At that time, we have a chance to fold it to the constant.

This optimization improves the performance of accessing to module namespace objects.

Before
    $ time ../../WebKitBuild/module-ic-tot/Release/bin/jsc -m module-assert-access-namespace.js
    ../../WebKitBuild/module-ic-tot/Release/bin/jsc -m   0.43s user 0.03s system 101% cpu 0.451 total
    $ time ../../WebKitBuild/module-ic-tot/Release/bin/jsc -m module-assert-access-binding.js
    ../../WebKitBuild/module-ic-tot/Release/bin/jsc -m   0.08s user 0.02s system 103% cpu 0.104 total

After
    $ time ../../WebKitBuild/module-ic/Release/bin/jsc -m module-assert-access-namespace.js
    ../../WebKitBuild/module-ic/Release/bin/jsc -m   0.11s user 0.01s system 106% cpu 0.109 total
    $ time ../../WebKitBuild/module-ic/Release/bin/jsc -m module-assert-access-binding.js
    ../../WebKitBuild/module-ic/Release/bin/jsc -m module-assert-access-binding.j  0.08s user 0.02s system 102% cpu 0.105 total

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/AccessCase.cpp:
(JSC::AccessCase::create):
(JSC::AccessCase::guardedByStructureCheck):
(JSC::AccessCase::canReplace):
(JSC::AccessCase::visitWeak):
(JSC::AccessCase::generateWithGuard):
(JSC::AccessCase::generateImpl):
* bytecode/AccessCase.h:
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::GetByIdStatus):
(JSC::GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback):
(JSC::GetByIdStatus::makesCalls):
(JSC::GetByIdStatus::dump):
* bytecode/GetByIdStatus.h:
(JSC::GetByIdStatus::isModuleNamespace):
(JSC::GetByIdStatus::takesSlowPath):
(JSC::GetByIdStatus::moduleNamespaceObject):
(JSC::GetByIdStatus::moduleEnvironment):
(JSC::GetByIdStatus::scopeOffset):
* bytecode/ModuleNamespaceAccessCase.cpp: Added.
(JSC::ModuleNamespaceAccessCase::ModuleNamespaceAccessCase):
(JSC::ModuleNamespaceAccessCase::create):
(JSC::ModuleNamespaceAccessCase::~ModuleNamespaceAccessCase):
(JSC::ModuleNamespaceAccessCase::clone):
(JSC::ModuleNamespaceAccessCase::emit):
* bytecode/ModuleNamespaceAccessCase.h: Added.
(JSC::ModuleNamespaceAccessCase::moduleNamespaceObject):
(JSC::ModuleNamespaceAccessCase::moduleEnvironment):
(JSC::ModuleNamespaceAccessCase::scopeOffset):
* bytecode/PolymorphicAccess.cpp:
(WTF::printInternal):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleModuleNamespaceLoad):
(JSC::DFG::ByteCodeParser::handleGetById):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::loadValue):
* jit/Repatch.cpp:
(JSC::tryCacheGetByID):
* runtime/AbstractModuleRecord.cpp:
(JSC::AbstractModuleRecord::getModuleNamespace):
* runtime/JSModuleNamespaceObject.cpp:
(JSC::JSModuleNamespaceObject::finishCreation):
(JSC::JSModuleNamespaceObject::visitChildren):
(JSC::getValue):
(JSC::JSModuleNamespaceObject::getOwnPropertySlot):
(JSC::JSModuleNamespaceObject::getOwnPropertyNames):
* runtime/JSModuleNamespaceObject.h:
(JSC::isJSModuleNamespaceObject):
(JSC::JSModuleNamespaceObject::create): Deleted.
(JSC::JSModuleNamespaceObject::createStructure): Deleted.
(JSC::JSModuleNamespaceObject::moduleRecord): Deleted.
* runtime/JSModuleRecord.h:
(JSC::JSModuleRecord::moduleEnvironment): Deleted.
* runtime/PropertySlot.h:
(JSC::PropertySlot::PropertySlot):
(JSC::PropertySlot::domJIT):
(JSC::PropertySlot::moduleNamespaceSlot):
(JSC::PropertySlot::setValueModuleNamespace):
(JSC::PropertySlot::setCacheableCustom):

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

30 files changed:
JSTests/ChangeLog
JSTests/modules/module-assert-access-binding.js [new file with mode: 0644]
JSTests/modules/module-assert-access-namespace.js [new file with mode: 0644]
JSTests/modules/namespace-empty.js [new file with mode: 0644]
JSTests/stress/module-namespace-access-change.js [new file with mode: 0644]
JSTests/stress/module-namespace-access-non-constant.js [new file with mode: 0644]
JSTests/stress/module-namespace-access-poly.js [new file with mode: 0644]
JSTests/stress/module-namespace-access-transitive-exports.js [new file with mode: 0644]
JSTests/stress/module-namespace-access.js [new file with mode: 0644]
JSTests/stress/resources/module-namespace-access-transitive-exports-2.js [new file with mode: 0644]
JSTests/stress/resources/module-namespace-access-transitive-exports.js [new file with mode: 0644]
JSTests/stress/resources/module-namespace-access.js [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/AccessCase.cpp
Source/JavaScriptCore/bytecode/AccessCase.h
Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
Source/JavaScriptCore/bytecode/GetByIdStatus.h
Source/JavaScriptCore/bytecode/ModuleNamespaceAccessCase.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/ModuleNamespaceAccessCase.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/Repatch.cpp
Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp
Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp
Source/JavaScriptCore/runtime/JSModuleNamespaceObject.h
Source/JavaScriptCore/runtime/JSModuleRecord.h
Source/JavaScriptCore/runtime/PropertySlot.h

index a062dbb..62e1ccd 100644 (file)
@@ -1,3 +1,40 @@
+2017-02-22  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        JSModuleNamespace object should have IC
+        https://bugs.webkit.org/show_bug.cgi?id=160590
+
+        Reviewed by Saam Barati.
+
+        * modules/module-assert-access-binding.js: Added.
+        * modules/module-assert-access-namespace.js: Added.
+        * modules/namespace-empty.js: Added.
+        (from.string_appeared_here.access):
+        (i.shouldThrow):
+        * stress/module-namespace-access-change.js: Added.
+        (shouldBe):
+        (access):
+        (import.string_appeared_here.then):
+        * stress/module-namespace-access-non-constant.js: Added.
+        (shouldBe):
+        (import.string_appeared_here.then):
+        * stress/module-namespace-access-poly.js: Added.
+        (shouldBe):
+        (access):
+        (import.string_appeared_here.then):
+        * stress/module-namespace-access-transitive-exports.js: Added.
+        (shouldBe):
+        (import.string_appeared_here.then):
+        * stress/module-namespace-access.js: Added.
+        (shouldBe):
+        (import.string_appeared_here.then):
+        * stress/resources/module-namespace-access-transitive-exports-2.js: Added.
+        (export.cocoa):
+        (export.change):
+        * stress/resources/module-namespace-access-transitive-exports.js: Added.
+        * stress/resources/module-namespace-access.js: Added.
+        (export.cocoa):
+        (export.change):
+
 2017-02-20  Filip Pizlo  <fpizlo@apple.com>
 
         The collector thread should only start when the mutator doesn't have heap access
diff --git a/JSTests/modules/module-assert-access-binding.js b/JSTests/modules/module-assert-access-binding.js
new file mode 100644 (file)
index 0000000..563cc9d
--- /dev/null
@@ -0,0 +1,10 @@
+import {shouldBe} from "./resources/assert.js";
+
+let array = [];
+for (let i = 0; i < 4000000; i++) {
+    array.push(i);
+}
+
+for (let i = 0; i < 4000000; i++) {
+    shouldBe(array[i], i);
+}
diff --git a/JSTests/modules/module-assert-access-namespace.js b/JSTests/modules/module-assert-access-namespace.js
new file mode 100644 (file)
index 0000000..fe090bd
--- /dev/null
@@ -0,0 +1,10 @@
+import * as assert from "./resources/assert.js";
+
+let array = [];
+for (let i = 0; i < 4000000; i++) {
+    array.push(i);
+}
+
+for (let i = 0; i < 4000000; i++) {
+    assert.shouldBe(array[i], i);
+}
diff --git a/JSTests/modules/namespace-empty.js b/JSTests/modules/namespace-empty.js
new file mode 100644 (file)
index 0000000..398048c
--- /dev/null
@@ -0,0 +1,17 @@
+import * as ns from "./namespace-empty.js"
+import {shouldThrow} from "./resources/assert.js"
+
+function access(ns)
+{
+    return ns.test;
+}
+noInline(access);
+
+for (var i = 0; i < 1e3; ++i) {
+    shouldThrow(() => {
+        access(ns);
+    }, `ReferenceError: Cannot access uninitialized variable.`);
+}
+
+
+export let test = 42;
diff --git a/JSTests/stress/module-namespace-access-change.js b/JSTests/stress/module-namespace-access-change.js
new file mode 100644 (file)
index 0000000..6e49567
--- /dev/null
@@ -0,0 +1,19 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function access(namespace)
+{
+    return namespace.test;
+}
+noInline(access);
+
+import("./resources/module-namespace-access.js").then((ns) => {
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(access(ns), 42)
+    ns.change();
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(access(ns), 55)
+});
+drainMicrotasks();
diff --git a/JSTests/stress/module-namespace-access-non-constant.js b/JSTests/stress/module-namespace-access-non-constant.js
new file mode 100644 (file)
index 0000000..02d7065
--- /dev/null
@@ -0,0 +1,13 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+import("./resources/module-namespace-access.js").then((ns) => {
+    ns.change();
+    for (var i = 0; i < 1e6; ++i) {
+        shouldBe(ns.test, 55);
+        shouldBe(ns.cocoa(), 55);
+    }
+});
+drainMicrotasks();
diff --git a/JSTests/stress/module-namespace-access-poly.js b/JSTests/stress/module-namespace-access-poly.js
new file mode 100644 (file)
index 0000000..5dfcac7
--- /dev/null
@@ -0,0 +1,24 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function access(ns)
+{
+    return ns.test;
+}
+noInline(access);
+
+import("./resources/module-namespace-access.js").then((ns) => {
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(access(ns), 42);
+    }
+    let nonNS = { test: 50 };
+    let nonNS2 = { ok: 22, test: 52 };
+    for (var i = 0; i < 1e4; ++i) {
+        shouldBe(access(ns), 42);
+        shouldBe(access(nonNS), 50);
+        shouldBe(access(nonNS2), 52);
+    }
+});
+drainMicrotasks();
diff --git a/JSTests/stress/module-namespace-access-transitive-exports.js b/JSTests/stress/module-namespace-access-transitive-exports.js
new file mode 100644 (file)
index 0000000..b74e5e5
--- /dev/null
@@ -0,0 +1,12 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+import("./resources/module-namespace-access-transitive-exports.js").then((ns) => {
+    for (var i = 0; i < 1e6; ++i) {
+        shouldBe(ns.test, 42);
+        shouldBe(ns.cocoa(), 42);
+    }
+});
+drainMicrotasks();
diff --git a/JSTests/stress/module-namespace-access.js b/JSTests/stress/module-namespace-access.js
new file mode 100644 (file)
index 0000000..2ac1409
--- /dev/null
@@ -0,0 +1,12 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+import("./resources/module-namespace-access.js").then((ns) => {
+    for (var i = 0; i < 1e6; ++i) {
+        shouldBe(ns.test, 42);
+        shouldBe(ns.cocoa(), 42);
+    }
+});
+drainMicrotasks();
diff --git a/JSTests/stress/resources/module-namespace-access-transitive-exports-2.js b/JSTests/stress/resources/module-namespace-access-transitive-exports-2.js
new file mode 100644 (file)
index 0000000..54df32c
--- /dev/null
@@ -0,0 +1,10 @@
+export let test = 42;
+export function cocoa()
+{
+    return test;
+}
+
+export function change()
+{
+    test = 55;
+}
diff --git a/JSTests/stress/resources/module-namespace-access-transitive-exports.js b/JSTests/stress/resources/module-namespace-access-transitive-exports.js
new file mode 100644 (file)
index 0000000..68019ca
--- /dev/null
@@ -0,0 +1 @@
+export * from "./module-namespace-access-transitive-exports-2.js"
diff --git a/JSTests/stress/resources/module-namespace-access.js b/JSTests/stress/resources/module-namespace-access.js
new file mode 100644 (file)
index 0000000..54df32c
--- /dev/null
@@ -0,0 +1,10 @@
+export let test = 42;
+export function cocoa()
+{
+    return test;
+}
+
+export function change()
+{
+    test = 55;
+}
index 9ca0795..810d361 100644 (file)
@@ -230,6 +230,7 @@ set(JavaScriptCore_SOURCES
     bytecode/LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp
     bytecode/LazyOperandValueProfile.cpp
     bytecode/MethodOfGettingAValueProfile.cpp
+    bytecode/ModuleNamespaceAccessCase.cpp
     bytecode/ModuleProgramCodeBlock.cpp
     bytecode/ObjectPropertyCondition.cpp
     bytecode/ObjectPropertyConditionSet.cpp
index 046c163..31aea12 100644 (file)
@@ -1,3 +1,107 @@
+2017-02-22  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        JSModuleNamespace object should have IC
+        https://bugs.webkit.org/show_bug.cgi?id=160590
+
+        Reviewed by Saam Barati.
+
+        This patch optimizes accesses to module namespace objects.
+
+        1. Cache the resolutions for module namespace objects.
+
+            When constructing the module namespace object, we already resolves all the exports.
+            The module namespace object caches this result and leverage it in the later access in
+            getOwnPropertySlot. This avoids resolving bindings through resolveExport.
+
+        2. Introduce ModuleNamespaceLoad IC.
+
+            This patch adds new IC for module namespace objects. The mechanism is simple, getOwnPropertySlot
+            tells us about module namespace object resolution. The IC first checks whether the given object
+            is an expected module namespace object. If this check succeeds, we load the value from the module
+            environment.
+
+        3. Introduce DFG/FTL optimization.
+
+            After exploiting module namespace object accesses in (2), DFG can recognize this in ByteCodeParser.
+            DFG will convert it to CheckCell with the namespace object and GetClosureVar from the cached environment.
+            At that time, we have a chance to fold it to the constant.
+
+        This optimization improves the performance of accessing to module namespace objects.
+
+        Before
+            $ time ../../WebKitBuild/module-ic-tot/Release/bin/jsc -m module-assert-access-namespace.js
+            ../../WebKitBuild/module-ic-tot/Release/bin/jsc -m   0.43s user 0.03s system 101% cpu 0.451 total
+            $ time ../../WebKitBuild/module-ic-tot/Release/bin/jsc -m module-assert-access-binding.js
+            ../../WebKitBuild/module-ic-tot/Release/bin/jsc -m   0.08s user 0.02s system 103% cpu 0.104 total
+
+        After
+            $ time ../../WebKitBuild/module-ic/Release/bin/jsc -m module-assert-access-namespace.js
+            ../../WebKitBuild/module-ic/Release/bin/jsc -m   0.11s user 0.01s system 106% cpu 0.109 total
+            $ time ../../WebKitBuild/module-ic/Release/bin/jsc -m module-assert-access-binding.js
+            ../../WebKitBuild/module-ic/Release/bin/jsc -m module-assert-access-binding.j  0.08s user 0.02s system 102% cpu 0.105 total
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/AccessCase.cpp:
+        (JSC::AccessCase::create):
+        (JSC::AccessCase::guardedByStructureCheck):
+        (JSC::AccessCase::canReplace):
+        (JSC::AccessCase::visitWeak):
+        (JSC::AccessCase::generateWithGuard):
+        (JSC::AccessCase::generateImpl):
+        * bytecode/AccessCase.h:
+        * bytecode/GetByIdStatus.cpp:
+        (JSC::GetByIdStatus::GetByIdStatus):
+        (JSC::GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback):
+        (JSC::GetByIdStatus::makesCalls):
+        (JSC::GetByIdStatus::dump):
+        * bytecode/GetByIdStatus.h:
+        (JSC::GetByIdStatus::isModuleNamespace):
+        (JSC::GetByIdStatus::takesSlowPath):
+        (JSC::GetByIdStatus::moduleNamespaceObject):
+        (JSC::GetByIdStatus::moduleEnvironment):
+        (JSC::GetByIdStatus::scopeOffset):
+        * bytecode/ModuleNamespaceAccessCase.cpp: Added.
+        (JSC::ModuleNamespaceAccessCase::ModuleNamespaceAccessCase):
+        (JSC::ModuleNamespaceAccessCase::create):
+        (JSC::ModuleNamespaceAccessCase::~ModuleNamespaceAccessCase):
+        (JSC::ModuleNamespaceAccessCase::clone):
+        (JSC::ModuleNamespaceAccessCase::emit):
+        * bytecode/ModuleNamespaceAccessCase.h: Added.
+        (JSC::ModuleNamespaceAccessCase::moduleNamespaceObject):
+        (JSC::ModuleNamespaceAccessCase::moduleEnvironment):
+        (JSC::ModuleNamespaceAccessCase::scopeOffset):
+        * bytecode/PolymorphicAccess.cpp:
+        (WTF::printInternal):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleModuleNamespaceLoad):
+        (JSC::DFG::ByteCodeParser::handleGetById):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::loadValue):
+        * jit/Repatch.cpp:
+        (JSC::tryCacheGetByID):
+        * runtime/AbstractModuleRecord.cpp:
+        (JSC::AbstractModuleRecord::getModuleNamespace):
+        * runtime/JSModuleNamespaceObject.cpp:
+        (JSC::JSModuleNamespaceObject::finishCreation):
+        (JSC::JSModuleNamespaceObject::visitChildren):
+        (JSC::getValue):
+        (JSC::JSModuleNamespaceObject::getOwnPropertySlot):
+        (JSC::JSModuleNamespaceObject::getOwnPropertyNames):
+        * runtime/JSModuleNamespaceObject.h:
+        (JSC::isJSModuleNamespaceObject):
+        (JSC::JSModuleNamespaceObject::create): Deleted.
+        (JSC::JSModuleNamespaceObject::createStructure): Deleted.
+        (JSC::JSModuleNamespaceObject::moduleRecord): Deleted.
+        * runtime/JSModuleRecord.h:
+        (JSC::JSModuleRecord::moduleEnvironment): Deleted.
+        * runtime/PropertySlot.h:
+        (JSC::PropertySlot::PropertySlot):
+        (JSC::PropertySlot::domJIT):
+        (JSC::PropertySlot::moduleNamespaceSlot):
+        (JSC::PropertySlot::setValueModuleNamespace):
+        (JSC::PropertySlot::setCacheableCustom):
+
 2017-02-22  Saam Barati  <sbarati@apple.com>
 
         Unreviewed. Rename AirGraphColoring.* files to AirAllocateRegistersByGraphColoring.* to be more consistent with the rest of the Air file names.
index 3e4d29f..3a31f86 100644 (file)
                FED94F2F171E3E2300BE77A4 /* Watchdog.h in Headers */ = {isa = PBXBuildFile; fileRef = FED94F2C171E3E2300BE77A4 /* Watchdog.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FEF040511AAE662D00BD28B0 /* CompareAndSwapTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEF040501AAE662D00BD28B0 /* CompareAndSwapTest.cpp */; };
                FEFD6FC61D5E7992008F2F0B /* JSStringInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = FEFD6FC51D5E7970008F2F0B /* JSStringInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               321D9E4CFB67423A97F191A7 /* ModuleNamespaceAccessCase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 20ECB15EFC524624BC2F02D5 /* ModuleNamespaceAccessCase.cpp */; };
+               9F63434577274FAFB9336C38 /* ModuleNamespaceAccessCase.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CE978E385A8498199052153 /* ModuleNamespaceAccessCase.h */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
                FEF040501AAE662D00BD28B0 /* CompareAndSwapTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CompareAndSwapTest.cpp; path = API/tests/CompareAndSwapTest.cpp; sourceTree = "<group>"; };
                FEF040521AAEC4ED00BD28B0 /* CompareAndSwapTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CompareAndSwapTest.h; path = API/tests/CompareAndSwapTest.h; sourceTree = "<group>"; };
                FEFD6FC51D5E7970008F2F0B /* JSStringInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSStringInlines.h; sourceTree = "<group>"; };
+               20ECB15EFC524624BC2F02D5 /* ModuleNamespaceAccessCase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ModuleNamespaceAccessCase.cpp; path = ModuleNamespaceAccessCase.cpp; sourceTree = "<group>"; };
+               4CE978E385A8498199052153 /* ModuleNamespaceAccessCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ModuleNamespaceAccessCase.h; path = ModuleNamespaceAccessCase.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
                                0F426A461460CBAB00131F8F /* VirtualRegister.h */,
                                0F919D2215853CDE004A4E7D /* Watchpoint.cpp */,
                                0F919D2315853CDE004A4E7D /* Watchpoint.h */,
+                               20ECB15EFC524624BC2F02D5 /* ModuleNamespaceAccessCase.cpp */,
+                               4CE978E385A8498199052153 /* ModuleNamespaceAccessCase.h */,
                        );
                        path = bytecode;
                        sourceTree = "<group>";
                                86704B8812DBA33700A9FE7B /* YarrParser.h in Headers */,
                                86704B8A12DBA33700A9FE7B /* YarrPattern.h in Headers */,
                                86704B4312DB8A8100A9FE7B /* YarrSyntaxChecker.h in Headers */,
+                               9F63434577274FAFB9336C38 /* ModuleNamespaceAccessCase.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                86704B8612DBA33700A9FE7B /* YarrJIT.cpp in Sources */,
                                86704B8912DBA33700A9FE7B /* YarrPattern.cpp in Sources */,
                                86704B4212DB8A8100A9FE7B /* YarrSyntaxChecker.cpp in Sources */,
+                               321D9E4CFB67423A97F191A7 /* ModuleNamespaceAccessCase.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index 16a1a53..658ea0f 100644 (file)
 #include "HeapInlines.h"
 #include "IntrinsicGetterAccessCase.h"
 #include "JSCJSValueInlines.h"
+#include "JSModuleEnvironment.h"
+#include "JSModuleNamespaceObject.h"
 #include "LinkBuffer.h"
+#include "ModuleNamespaceAccessCase.h"
 #include "PolymorphicAccess.h"
 #include "ScopedArguments.h"
 #include "ScratchRegisterAllocator.h"
@@ -65,6 +68,7 @@ std::unique_ptr<AccessCase> AccessCase::create(VM& vm, JSCell* owner, AccessType
     case StringLength:
     case DirectArgumentsLength:
     case ScopedArgumentsLength:
+    case ModuleNamespaceLoad:
     case Replace:
         break;
     default:
@@ -148,6 +152,7 @@ bool AccessCase::guardedByStructureCheck() const
     case StringLength:
     case DirectArgumentsLength:
     case ScopedArgumentsLength:
+    case ModuleNamespaceLoad:
         return false;
     default:
         return true;
@@ -193,6 +198,13 @@ bool AccessCase::canReplace(const AccessCase& other) const
     case DirectArgumentsLength:
     case ScopedArgumentsLength:
         return other.type() == type();
+    case ModuleNamespaceLoad: {
+        if (other.type() != type())
+            return false;
+        auto& thisCase = this->as<ModuleNamespaceAccessCase>();
+        auto& otherCase = this->as<ModuleNamespaceAccessCase>();
+        return thisCase.moduleNamespaceObject() == otherCase.moduleNamespaceObject();
+    }
     default:
         if (!guardedByStructureCheck() || !other.guardedByStructureCheck())
             return false;
@@ -239,6 +251,12 @@ bool AccessCase::visitWeak(VM& vm) const
         auto& intrinsic = this->as<IntrinsicGetterAccessCase>();
         if (intrinsic.intrinsicFunction() && !Heap::isMarked(intrinsic.intrinsicFunction()))
             return false;
+    } else if (type() == ModuleNamespaceLoad) {
+        auto& accessCase = this->as<ModuleNamespaceAccessCase>();
+        if (accessCase.moduleNamespaceObject() && !Heap::isMarked(accessCase.moduleNamespaceObject()))
+            return false;
+        if (accessCase.moduleEnvironment() && !Heap::isMarked(accessCase.moduleEnvironment()))
+            return false;
     }
 
     return true;
@@ -344,6 +362,11 @@ void AccessCase::generateWithGuard(
         return;
     }
 
+    case ModuleNamespaceLoad: {
+        this->as<ModuleNamespaceAccessCase>().emit(state, fallThrough);
+        return;
+    }
+
     default: {
         if (viaProxy()) {
             fallThrough.append(
@@ -991,6 +1014,7 @@ void AccessCase::generateImpl(AccessGenerationState& state)
         
     case DirectArgumentsLength:
     case ScopedArgumentsLength:
+    case ModuleNamespaceLoad:
         // These need to be handled by generateWithGuard(), since the guard is part of the
         // algorithm. We can be sure that nobody will call generate() directly for these since they
         // are not guarded by structure checks.
index 29e1adf..9f8a200 100644 (file)
@@ -95,7 +95,8 @@ public:
         ArrayLength,
         StringLength,
         DirectArgumentsLength,
-        ScopedArgumentsLength
+        ScopedArgumentsLength,
+        ModuleNamespaceLoad,
     };
 
     enum State : uint8_t {
index eb571ff..1537cd9 100644 (file)
@@ -34,6 +34,7 @@
 #include "JSScope.h"
 #include "LLIntData.h"
 #include "LowLevelInterpreter.h"
+#include "ModuleNamespaceAccessCase.h"
 #include "PolymorphicAccess.h"
 #include "StructureStubInfo.h"
 #include <wtf/ListDump.h>
@@ -147,6 +148,15 @@ GetByIdStatus GetByIdStatus::computeForStubInfo(const ConcurrentJSLocker& locker
 #endif // ENABLE(DFG_JIT)
 
 #if ENABLE(JIT)
+GetByIdStatus::GetByIdStatus(const ModuleNamespaceAccessCase& accessCase)
+    : m_state(ModuleNamespace)
+    , m_wasSeenInJIT(true)
+    , m_moduleNamespaceObject(accessCase.moduleNamespaceObject())
+    , m_moduleEnvironment(accessCase.moduleEnvironment())
+    , m_scopeOffset(accessCase.scopeOffset())
+{
+}
+
 GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
     const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, UniquedStringImpl* uid,
     CallLinkStatus::ExitSiteData callExitSiteData)
@@ -195,6 +205,16 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
     }
         
     case CacheType::Stub: {
+        if (list->size() == 1) {
+            const AccessCase& access = list->at(0);
+            switch (access.type()) {
+            case AccessCase::ModuleNamespaceLoad:
+                return GetByIdStatus(access.as<ModuleNamespaceAccessCase>());
+            default:
+                break;
+            }
+        }
+
         for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) {
             const AccessCase& access = list->at(listIndex);
             if (access.viaProxy())
@@ -376,6 +396,7 @@ bool GetByIdStatus::makesCalls() const
     case NoInformation:
     case TakesSlowPath:
     case Custom:
+    case ModuleNamespace:
         return false;
     case Simple:
         for (unsigned i = m_variants.size(); i--;) {
@@ -420,6 +441,9 @@ void GetByIdStatus::dump(PrintStream& out) const
     case Custom:
         out.print("Custom");
         break;
+    case ModuleNamespace:
+        out.print("ModuleNamespace");
+        break;
     case TakesSlowPath:
         out.print("TakesSlowPath");
         break;
index e8aad89..de47bf5 100644 (file)
 #include "ConcurrentJSLock.h"
 #include "ExitingJITType.h"
 #include "GetByIdVariant.h"
+#include "ScopeOffset.h"
 
 namespace JSC {
 
+class AccessCase;
 class CodeBlock;
+class JSModuleEnvironment;
+class JSModuleNamespaceObject;
+class ModuleNamespaceAccessCase;
 class StructureStubInfo;
 
 typedef HashMap<CodeOrigin, StructureStubInfo*, CodeOriginApproximateHash> StubInfoMap;
@@ -41,12 +46,19 @@ typedef HashMap<CodeOrigin, StructureStubInfo*, CodeOriginApproximateHash> StubI
 class GetByIdStatus {
 public:
     enum State {
-        NoInformation,  // It's uncached so we have no information.
-        Simple,         // It's cached for a simple access to a known object property with
-                        // a possible structure chain and a possible specific value.
-        Custom,         // It's cached for a custom accessor with a possible structure chain.
-        TakesSlowPath,  // It's known to often take slow path.
-        MakesCalls      // It's known to take paths that make calls.
+        // 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 cached for a custom accessor with a possible structure chain.
+        Custom,
+        // It's cached for an access to a module namespace object's binding.
+        ModuleNamespace,
+        // It's known to often take slow path.
+        TakesSlowPath,
+        // It's known to take paths that make calls.
+        MakesCalls,
     };
 
     GetByIdStatus()
@@ -59,6 +71,7 @@ public:
     {
         ASSERT(state == NoInformation || state == TakesSlowPath || state == MakesCalls);
     }
+
     
     GetByIdStatus(
         State state, bool wasSeenInJIT, const GetByIdVariant& variant = GetByIdVariant())
@@ -84,19 +97,24 @@ public:
     bool operator!() const { return !isSet(); }
     bool isSimple() const { return m_state == Simple; }
     bool isCustom() const { return m_state == Custom; }
+    bool isModuleNamespace() const { return m_state == ModuleNamespace; }
 
     size_t numVariants() const { return m_variants.size(); }
     const Vector<GetByIdVariant, 1>& variants() const { return m_variants; }
     const GetByIdVariant& at(size_t index) const { return m_variants[index]; }
     const GetByIdVariant& operator[](size_t index) const { return at(index); }
 
-    bool takesSlowPath() const { return m_state == TakesSlowPath || m_state == MakesCalls || m_state == Custom; }
+    bool takesSlowPath() const { return m_state == TakesSlowPath || m_state == MakesCalls || m_state == Custom || m_state == ModuleNamespace; }
     bool makesCalls() const;
     
     bool wasSeenInJIT() const { return m_wasSeenInJIT; }
     
     // Attempts to reduce the set of variants to fit the given structure set. This may be approximate.
     void filter(const StructureSet&);
+
+    JSModuleNamespaceObject* moduleNamespaceObject() const { return m_moduleNamespaceObject; }
+    JSModuleEnvironment* moduleEnvironment() const { return m_moduleEnvironment; }
+    ScopeOffset scopeOffset() const { return m_scopeOffset; }
     
     void dump(PrintStream&) const;
     
@@ -105,6 +123,7 @@ private:
     static bool hasExitSite(const ConcurrentJSLocker&, CodeBlock*, unsigned bytecodeIndex);
 #endif
 #if ENABLE(JIT)
+    GetByIdStatus(const ModuleNamespaceAccessCase&);
     static GetByIdStatus computeForStubInfoWithoutExitSiteFeedback(
         const ConcurrentJSLocker&, CodeBlock* profiledBlock, StructureStubInfo*,
         UniquedStringImpl* uid, CallLinkStatus::ExitSiteData);
@@ -116,6 +135,9 @@ private:
     State m_state;
     Vector<GetByIdVariant, 1> m_variants;
     bool m_wasSeenInJIT;
+    JSModuleNamespaceObject* m_moduleNamespaceObject { nullptr };
+    JSModuleEnvironment* m_moduleEnvironment { nullptr };
+    ScopeOffset m_scopeOffset { };
 };
 
 } // namespace JSC
diff --git a/Source/JavaScriptCore/bytecode/ModuleNamespaceAccessCase.cpp b/Source/JavaScriptCore/bytecode/ModuleNamespaceAccessCase.cpp
new file mode 100644 (file)
index 0000000..3c168c6
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 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 "ModuleNamespaceAccessCase.h"
+
+#if ENABLE(JIT)
+
+#include "CCallHelpers.h"
+#include "HeapInlines.h"
+#include "JSModuleEnvironment.h"
+#include "JSModuleNamespaceObject.h"
+#include "PolymorphicAccess.h"
+#include "StructureStubInfo.h"
+
+namespace JSC {
+
+ModuleNamespaceAccessCase::ModuleNamespaceAccessCase(VM& vm, JSCell* owner, JSModuleNamespaceObject* moduleNamespaceObject, JSModuleEnvironment* moduleEnvironment, ScopeOffset scopeOffset)
+    : Base(vm, owner, ModuleNamespaceLoad, invalidOffset, nullptr, ObjectPropertyConditionSet())
+    , m_scopeOffset(scopeOffset)
+{
+    m_moduleNamespaceObject.set(vm, owner, moduleNamespaceObject);
+    m_moduleEnvironment.set(vm, owner, moduleEnvironment);
+}
+
+std::unique_ptr<AccessCase> ModuleNamespaceAccessCase::create(VM& vm, JSCell* owner, JSModuleNamespaceObject* moduleNamespaceObject, JSModuleEnvironment* moduleEnvironment, ScopeOffset scopeOffset)
+{
+    return std::unique_ptr<AccessCase>(new ModuleNamespaceAccessCase(vm, owner, moduleNamespaceObject, moduleEnvironment, scopeOffset));
+}
+
+ModuleNamespaceAccessCase::~ModuleNamespaceAccessCase()
+{
+}
+
+std::unique_ptr<AccessCase> ModuleNamespaceAccessCase::clone() const
+{
+    std::unique_ptr<ModuleNamespaceAccessCase> result(new ModuleNamespaceAccessCase(*this));
+    result->resetState();
+    return WTFMove(result);
+}
+
+void ModuleNamespaceAccessCase::emit(AccessGenerationState& state, MacroAssembler::JumpList& fallThrough)
+{
+    CCallHelpers& jit = *state.jit;
+    JSValueRegs valueRegs = state.valueRegs;
+    GPRReg baseGPR = state.baseGPR;
+
+    fallThrough.append(
+        jit.branchPtr(
+            CCallHelpers::NotEqual,
+            baseGPR,
+            CCallHelpers::TrustedImmPtr(m_moduleNamespaceObject.get())));
+
+    jit.loadValue(&m_moduleEnvironment->variableAt(m_scopeOffset), valueRegs);
+    state.failAndIgnore.append(jit.branchIfEmpty(valueRegs));
+    state.succeed();
+}
+
+
+} // namespace JSC
+
+#endif // ENABLE(JIT)
diff --git a/Source/JavaScriptCore/bytecode/ModuleNamespaceAccessCase.h b/Source/JavaScriptCore/bytecode/ModuleNamespaceAccessCase.h
new file mode 100644 (file)
index 0000000..333075f
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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
+
+#if ENABLE(JIT)
+
+#include "AccessCase.h"
+
+namespace JSC {
+
+class JSModuleEnvironment;
+class JSModuleNamespaceObject;
+
+class ModuleNamespaceAccessCase : public AccessCase {
+public:
+    using Base = AccessCase;
+    friend class AccessCase;
+
+    JSModuleNamespaceObject* moduleNamespaceObject() const { return m_moduleNamespaceObject.get(); }
+    JSModuleEnvironment* moduleEnvironment() const { return m_moduleEnvironment.get(); }
+    ScopeOffset scopeOffset() const { return m_scopeOffset; }
+
+    static std::unique_ptr<AccessCase> create(VM&, JSCell* owner, JSModuleNamespaceObject*, JSModuleEnvironment*, ScopeOffset);
+
+    std::unique_ptr<AccessCase> clone() const override;
+
+    void emit(AccessGenerationState&, MacroAssembler::JumpList& fallThrough);
+
+    ~ModuleNamespaceAccessCase();
+
+private:
+    ModuleNamespaceAccessCase(VM&, JSCell* owner, JSModuleNamespaceObject*, JSModuleEnvironment*, ScopeOffset);
+
+    WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject;
+    WriteBarrier<JSModuleEnvironment> m_moduleEnvironment;
+    ScopeOffset m_scopeOffset;
+};
+
+} // namespace JSC
+
+#endif // ENABLE(JIT)
index 71861e7..4a7c309 100644 (file)
@@ -646,6 +646,9 @@ void printInternal(PrintStream& out, AccessCase::AccessType type)
     case AccessCase::ScopedArgumentsLength:
         out.print("ScopedArgumentsLength");
         return;
+    case AccessCase::ModuleNamespaceLoad:
+        out.print("ModuleNamespaceLoad");
+        return;
     }
 
     RELEASE_ASSERT_NOT_REACHED();
index 882c9e0..584deeb 100644 (file)
@@ -46,6 +46,7 @@
 #include "Heap.h"
 #include "JSCInlines.h"
 #include "JSModuleEnvironment.h"
+#include "JSModuleNamespaceObject.h"
 #include "NumberConstructor.h"
 #include "ObjectConstructor.h"
 #include "PreciseJumpTargets.h"
@@ -223,6 +224,7 @@ private:
     Node* handlePutByOffset(Node* base, unsigned identifier, PropertyOffset, const InferredType::Descriptor&, Node* value);
     Node* handleGetByOffset(SpeculatedType, Node* base, unsigned identifierNumber, PropertyOffset, const InferredType::Descriptor&, NodeType = GetByOffset);
     bool handleDOMJITGetter(int resultOperand, const GetByIdVariant&, Node* thisNode, unsigned identifierNumber, SpeculatedType prediction);
+    bool handleModuleNamespaceLoad(int resultOperand, SpeculatedType, Node* base, GetByIdStatus);
 
     // Create a presence ObjectPropertyCondition based on some known offset and structure set. Does not
     // check the validity of the condition, but it may return a null one if it encounters a contradiction.
@@ -2844,6 +2846,34 @@ bool ByteCodeParser::handleDOMJITGetter(int resultOperand, const GetByIdVariant&
     return true;
 }
 
+bool ByteCodeParser::handleModuleNamespaceLoad(int resultOperand, SpeculatedType prediction, Node* base, GetByIdStatus getById)
+{
+    if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCell))
+        return false;
+    addToGraph(CheckCell, OpInfo(m_graph.freeze(getById.moduleNamespaceObject())), Edge(base, CellUse));
+
+    // Ideally we wouldn't have to do this Phantom. But:
+    //
+    // For the constant case: we must do it because otherwise we would have no way of knowing
+    // that the scope is live at OSR here.
+    //
+    // For the non-constant case: GetClosureVar could be DCE'd, but baseline's implementation
+    // won't be able to handle an Undefined scope.
+    addToGraph(Phantom, base);
+
+    // Constant folding in the bytecode parser is important for performance. This may not
+    // have executed yet. If it hasn't, then we won't have a prediction. Lacking a
+    // prediction, we'd otherwise think that it has to exit. Then when it did execute, we
+    // would recompile. But if we can fold it here, we avoid the exit.
+    m_graph.freeze(getById.moduleEnvironment());
+    if (JSValue value = m_graph.tryGetConstantClosureVar(getById.moduleEnvironment(), getById.scopeOffset())) {
+        set(VirtualRegister(resultOperand), weakJSConstant(value));
+        return true;
+    }
+    set(VirtualRegister(resultOperand), addToGraph(GetClosureVar, OpInfo(getById.scopeOffset().offset()), OpInfo(prediction), weakJSConstant(getById.moduleEnvironment())));
+    return true;
+}
+
 template<typename ChecksFunctor>
 bool ByteCodeParser::handleTypedArrayConstructor(
     int resultOperand, InternalFunction* function, int registerOffset,
@@ -3438,6 +3468,14 @@ void ByteCodeParser::handleGetById(
     else
         getById = TryGetById;
 
+    if (getById != TryGetById && getByIdStatus.isModuleNamespace()) {
+        if (handleModuleNamespaceLoad(destinationOperand, prediction, base, getByIdStatus)) {
+            if (m_graph.compilation())
+                m_graph.compilation()->noticeInlinedGetById();
+            return;
+        }
+    }
+
     // Special path for custom accessors since custom's offset does not have any meanings.
     // So, this is completely different from Simple one. But we have a chance to optimize it when we use DOMJIT.
     if (Options::useDOMJIT() && getByIdStatus.isCustom()) {
index fb6376a..49c7f9c 100644 (file)
@@ -158,6 +158,16 @@ public:
         }
 #endif
     }
+
+    void loadValue(void* address, JSValueRegs regs)
+    {
+#if USE(JSVALUE64)
+        load64(address, regs.gpr());
+#else
+        load32(bitwise_cast<void*>(bitwise_cast<uintptr_t>(address) + PayloadOffset), regs.payloadGPR());
+        load32(bitwise_cast<void*>(bitwise_cast<uintptr_t>(address) + TagOffset), regs.tagGPR());
+#endif
+    }
     
     // Note that this clobbers offset.
     void loadProperty(GPRReg object, GPRReg offset, JSValueRegs result);
index cf11b50..ef85963 100644 (file)
 #include "JIT.h"
 #include "JITInlines.h"
 #include "JSCInlines.h"
+#include "JSModuleNamespaceObject.h"
 #include "JSWebAssembly.h"
 #include "LinkBuffer.h"
+#include "ModuleNamespaceAccessCase.h"
 #include "PolymorphicAccess.h"
 #include "ScopedArguments.h"
 #include "ScratchRegisterAllocator.h"
@@ -193,6 +195,11 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
                 newCase = AccessCase::create(vm, codeBlock, AccessCase::ScopedArgumentsLength);
         }
     }
+
+    if (!propertyName.isSymbol() && isJSModuleNamespaceObject(baseValue) && !slot.isUnset()) {
+        if (auto moduleNamespaceSlot = slot.moduleNamespaceSlot())
+            newCase = ModuleNamespaceAccessCase::create(vm, codeBlock, jsCast<JSModuleNamespaceObject*>(baseValue), moduleNamespaceSlot->environment, ScopeOffset(moduleNamespaceSlot->scopeOffset));
+    }
     
     if (!newCase) {
         if (!slot.isCacheable() && !slot.isUnset())
index 13129a6..f8854e2 100644 (file)
@@ -697,9 +697,10 @@ JSModuleNamespaceObject* AbstractModuleRecord::getModuleNamespace(ExecState* exe
     IdentifierSet exportedNames;
     getExportedNames(exec, this, exportedNames);
 
-    IdentifierSet unambiguousNames;
+    Vector<std::pair<Identifier, Resolution>> resolutions;
     for (auto& name : exportedNames) {
-        const AbstractModuleRecord::Resolution resolution = resolveExport(exec, Identifier::fromUid(exec, name.get()));
+        Identifier ident = Identifier::fromUid(exec, name.get());
+        const Resolution resolution = resolveExport(exec, ident);
         switch (resolution.type) {
         case Resolution::Type::NotFound:
             throwSyntaxError(exec, scope, makeString("Exported binding name '", String(name.get()), "' is not found."));
@@ -713,12 +714,12 @@ JSModuleNamespaceObject* AbstractModuleRecord::getModuleNamespace(ExecState* exe
             break;
 
         case Resolution::Type::Resolved:
-            unambiguousNames.add(name);
+            resolutions.append({ WTFMove(ident), resolution });
             break;
         }
     }
 
-    m_moduleNamespaceObject.set(vm, this, JSModuleNamespaceObject::create(exec, globalObject, globalObject->moduleNamespaceObjectStructure(), this, unambiguousNames));
+    m_moduleNamespaceObject.set(vm, this, JSModuleNamespaceObject::create(exec, globalObject, globalObject->moduleNamespaceObjectStructure(), this, WTFMove(resolutions)));
     return m_moduleNamespaceObject.get();
 }
 
index 797dfea..3606882 100644 (file)
@@ -41,7 +41,7 @@ JSModuleNamespaceObject::JSModuleNamespaceObject(VM& vm, Structure* structure)
 {
 }
 
-void JSModuleNamespaceObject::finishCreation(ExecState* exec, JSGlobalObject*, AbstractModuleRecord* moduleRecord, const IdentifierSet& exports)
+void JSModuleNamespaceObject::finishCreation(ExecState* exec, JSGlobalObject*, AbstractModuleRecord* moduleRecord, Vector<std::pair<Identifier, AbstractModuleRecord::Resolution>>&& resolutions)
 {
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
@@ -54,17 +54,25 @@ void JSModuleNamespaceObject::finishCreation(ExecState* exec, JSGlobalObject*, A
     //     The list is ordered as if an Array of those String values had been sorted using Array.prototype.sort using SortCompare as comparefn.
     //
     // Sort the exported names by the code point order.
-    Vector<UniquedStringImpl*> temporaryVector(exports.size(), nullptr);
-    std::transform(exports.begin(), exports.end(), temporaryVector.begin(), [](const RefPtr<WTF::UniquedStringImpl>& ref) {
-        return ref.get();
+    std::sort(resolutions.begin(), resolutions.end(), [] (const auto& lhs, const auto& rhs) {
+        return codePointCompare(lhs.first.impl(), rhs.first.impl()) < 0;
     });
-    std::sort(temporaryVector.begin(), temporaryVector.end(), [] (UniquedStringImpl* lhs, UniquedStringImpl* rhs) {
-        return codePointCompare(lhs, rhs) < 0;
-    });
-    for (auto* identifier : temporaryVector)
-        m_exports.add(identifier);
 
     m_moduleRecord.set(vm, this, moduleRecord);
+    {
+        unsigned moduleRecordOffset = 0;
+        m_names.reserveCapacity(resolutions.size());
+        for (const auto& pair : resolutions) {
+            moduleRecordAt(moduleRecordOffset).set(vm, this, pair.second.moduleRecord);
+            m_names.append(pair.first);
+            m_exports.add(pair.first.impl(), ExportEntry {
+                pair.second.localName,
+                moduleRecordOffset
+            });
+            ++moduleRecordOffset;
+        }
+    }
+
     putDirect(vm, vm.propertyNames->toStringTagSymbol, jsString(&vm, "Module"), DontEnum | DontDelete | ReadOnly);
 
     // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-getprototypeof
@@ -87,6 +95,22 @@ void JSModuleNamespaceObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     Base::visitChildren(thisObject, visitor);
     visitor.append(thisObject->m_moduleRecord);
+    for (unsigned i = 0; i < thisObject->m_names.size(); ++i)
+        visitor.appendHidden(thisObject->moduleRecordAt(i));
+}
+
+static JSValue getValue(JSModuleEnvironment* environment, PropertyName localName, ScopeOffset& scopeOffset)
+{
+    SymbolTable* symbolTable = environment->symbolTable();
+    {
+        ConcurrentJSLocker locker(symbolTable->m_lock);
+        auto iter = symbolTable->find(locker, localName.uid());
+        ASSERT(iter != symbolTable->end(locker));
+        SymbolTableEntry& entry = iter->value;
+        ASSERT(!entry.isNull());
+        scopeOffset = entry.scopeOffset();
+    }
+    return environment->variableAt(scopeOffset).get();
 }
 
 bool JSModuleNamespaceObject::getOwnPropertySlot(JSObject* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
@@ -104,40 +128,29 @@ bool JSModuleNamespaceObject::getOwnPropertySlot(JSObject* cell, ExecState* exec
     if (propertyName.isSymbol())
         return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
 
-    // FIXME: Add IC for module namespace object.
-    // https://bugs.webkit.org/show_bug.cgi?id=160590
-    slot.disableCaching();
     slot.setIsTaintedByOpaqueObject();
-    if (!thisObject->m_exports.contains(propertyName.uid()))
+
+    auto iterator = thisObject->m_exports.find(propertyName.uid());
+    if (iterator == thisObject->m_exports.end())
         return false;
+    ExportEntry& exportEntry = iterator->value;
 
     switch (slot.internalMethodType()) {
-    case PropertySlot::InternalMethodType::Get:
-    case PropertySlot::InternalMethodType::GetOwnProperty: {
-        AbstractModuleRecord* moduleRecord = thisObject->moduleRecord();
-
-        AbstractModuleRecord::Resolution resolution = moduleRecord->resolveExport(exec, Identifier::fromUid(exec, propertyName.uid()));
-        ASSERT(resolution.type != AbstractModuleRecord::Resolution::Type::NotFound && resolution.type != AbstractModuleRecord::Resolution::Type::Ambiguous);
-
-        AbstractModuleRecord* targetModule = resolution.moduleRecord;
-        JSModuleEnvironment* targetEnvironment = targetModule->moduleEnvironment();
-
-        PropertySlot trampolineSlot(targetEnvironment, PropertySlot::InternalMethodType::Get);
-        bool found = targetEnvironment->methodTable(vm)->getOwnPropertySlot(targetEnvironment, exec, resolution.localName, trampolineSlot);
-        ASSERT_UNUSED(found, found);
-
-        JSValue value = trampolineSlot.getValue(exec, propertyName);
-        ASSERT(!scope.exception());
-
+    case PropertySlot::InternalMethodType::GetOwnProperty:
+    case PropertySlot::InternalMethodType::Get: {
+        JSModuleEnvironment* environment = thisObject->moduleRecordAt(exportEntry.moduleRecordOffset)->moduleEnvironment();
+        ScopeOffset scopeOffset;
+        JSValue value = getValue(environment, exportEntry.localName, scopeOffset);
         // If the value is filled with TDZ value, throw a reference error.
         if (!value) {
             throwVMError(exec, scope, createTDZError(exec));
             return false;
         }
 
-        slot.setValue(thisObject, DontDelete, value);
+        slot.setValueModuleNamespace(thisObject, DontDelete, value, environment, scopeOffset);
         return true;
     }
+
     case PropertySlot::InternalMethodType::HasProperty: {
         // Do not perform [[Get]] for [[HasProperty]].
         // [[Get]] / [[GetOwnProperty]] onto namespace object could throw an error while [[HasProperty]] just returns true here.
@@ -189,8 +202,8 @@ void JSModuleNamespaceObject::getOwnPropertyNames(JSObject* cell, ExecState* exe
 {
     // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-ownpropertykeys
     JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
-    for (const auto& name : thisObject->m_exports)
-        propertyNames.add(name.get());
+    for (const auto& name : thisObject->m_names)
+        propertyNames.add(name.impl());
     return JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
 }
 
index b72f543..fb8ed37 100644 (file)
 
 #pragma once
 
+#include "AbstractModuleRecord.h"
 #include "JSDestructibleObject.h"
-#include <wtf/ListHashSet.h>
+#include "ScopeOffset.h"
 
 namespace JSC {
 
-class AbstractModuleRecord;
-
-class JSModuleNamespaceObject : public JSDestructibleObject {
+class JSModuleNamespaceObject final : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
     static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames | GetOwnPropertySlotIsImpureForPropertyAbsence | IsImmutablePrototypeExoticObject;
 
-    static JSModuleNamespaceObject* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, AbstractModuleRecord* moduleRecord, const IdentifierSet& exports)
+    static JSModuleNamespaceObject* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, AbstractModuleRecord* moduleRecord, Vector<std::pair<Identifier, AbstractModuleRecord::Resolution>>&& resolutions)
     {
-        JSModuleNamespaceObject* object = new (NotNull, allocateCell<JSModuleNamespaceObject>(exec->vm().heap)) JSModuleNamespaceObject(exec->vm(), structure);
-        object->finishCreation(exec, globalObject, moduleRecord, exports);
+        JSModuleNamespaceObject* object =
+            new (
+                NotNull,
+                allocateCell<JSModuleNamespaceObject>(exec->vm().heap, JSModuleNamespaceObject::allocationSize(resolutions.size())))
+            JSModuleNamespaceObject(exec->vm(), structure);
+        object->finishCreation(exec, globalObject, moduleRecord, WTFMove(resolutions));
         return object;
     }
 
@@ -61,17 +64,50 @@ public:
     AbstractModuleRecord* moduleRecord() { return m_moduleRecord.get(); }
 
 protected:
-    JS_EXPORT_PRIVATE void finishCreation(ExecState*, JSGlobalObject*, AbstractModuleRecord*, const IdentifierSet& exports);
+    JS_EXPORT_PRIVATE void finishCreation(ExecState*, JSGlobalObject*, AbstractModuleRecord*, Vector<std::pair<Identifier, AbstractModuleRecord::Resolution>>&&);
     JS_EXPORT_PRIVATE JSModuleNamespaceObject(VM&, Structure*);
 
 private:
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
 
-    typedef WTF::ListHashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> OrderedIdentifierSet;
+    WriteBarrierBase<AbstractModuleRecord>& moduleRecordAt(unsigned offset)
+    {
+        return moduleRecords()[offset];
+    }
+
+    WriteBarrierBase<AbstractModuleRecord>* moduleRecords()
+    {
+        return bitwise_cast<WriteBarrierBase<AbstractModuleRecord>*>(bitwise_cast<char*>(this) + offsetOfModuleRecords());
+    }
+
+    static size_t offsetOfModuleRecords()
+    {
+        return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<AbstractModuleRecord>)>(sizeof(JSModuleNamespaceObject));
+    }
+
+    static size_t allocationSize(unsigned moduleRecords)
+    {
+        return offsetOfModuleRecords() + moduleRecords * sizeof(WriteBarrier<AbstractModuleRecord>);
+    }
+
+    struct ExportEntry {
+        Identifier localName;
+        unsigned moduleRecordOffset;
+    };
+
+    typedef HashMap<RefPtr<UniquedStringImpl>, ExportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ExportMap;
 
-    OrderedIdentifierSet m_exports;
+    ExportMap m_exports;
+    Vector<Identifier> m_names;
     WriteBarrier<AbstractModuleRecord> m_moduleRecord;
 };
 
+inline bool isJSModuleNamespaceObject(JSCell* cell)
+{
+    return cell->classInfo(*cell->vm()) == JSModuleNamespaceObject::info();
+}
+
+inline bool isJSModuleNamespaceObject(JSValue v) { return v.isCell() && isJSModuleNamespaceObject(v.asCell()); }
+
 } // namespace JSC
index 9e69b0f..020947b 100644 (file)
@@ -40,11 +40,6 @@ public:
 
     DECLARE_EXPORT_INFO;
 
-    JSModuleEnvironment* moduleEnvironment()
-    {
-        ASSERT(m_moduleEnvironment);
-        return m_moduleEnvironment.get();
-    }
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
     static JSModuleRecord* create(ExecState*, VM&, Structure*, const Identifier&, const SourceCode&, const VariableEnvironment&, const VariableEnvironment&);
 
index 387e31b..a7f0a60 100644 (file)
@@ -23,6 +23,7 @@
 #include "JSCJSValue.h"
 #include "PropertyName.h"
 #include "PropertyOffset.h"
+#include "ScopeOffset.h"
 #include <wtf/Assertions.h>
 
 namespace JSC {
@@ -32,6 +33,7 @@ class GetterSetter;
 class ExecState;
 class GetterSetter;
 class JSObject;
+class JSModuleEnvironment;
 
 // ECMA 262-3 8.6.1
 // Property attributes
@@ -86,6 +88,12 @@ public:
         VMInquiry, // Our VM is just poking around. When this is the InternalMethodType, getOwnPropertySlot is not allowed to do user observable actions.
     };
 
+    enum class AdditionalDataType : uint8_t {
+        None,
+        DOMJIT, // Annotated with DOMJIT information.
+        ModuleNamespace, // ModuleNamespaceObject's environment access.
+    };
+
     explicit PropertySlot(const JSValue thisValue, InternalMethodType internalMethodType)
         : m_offset(invalidOffset)
         , m_thisValue(thisValue)
@@ -94,6 +102,7 @@ public:
         , m_cacheability(CachingAllowed)
         , m_propertyType(TypeUnset)
         , m_internalMethodType(internalMethodType)
+        , m_additionalDataType(AdditionalDataType::None)
         , m_isTaintedByOpaqueObject(false)
     {
     }
@@ -163,7 +172,21 @@ public:
 
     DOMJIT::GetterSetter* domJIT() const
     {
-        return m_domJIT;
+        if (m_additionalDataType == AdditionalDataType::DOMJIT)
+            return m_additionalData.domJIT;
+        return nullptr;
+    }
+
+    struct ModuleNamespaceSlot {
+        JSModuleEnvironment* environment;
+        unsigned scopeOffset;
+    };
+
+    std::optional<ModuleNamespaceSlot> moduleNamespaceSlot() const
+    {
+        if (m_additionalDataType == AdditionalDataType::ModuleNamespace)
+            return m_additionalData.moduleNamespaceSlot;
+        return std::nullopt;
     }
 
     void setValue(JSObject* slotBase, unsigned attributes, JSValue value)
@@ -206,6 +229,14 @@ public:
         m_offset = invalidOffset;
     }
 
+    void setValueModuleNamespace(JSObject* slotBase, unsigned attributes, JSValue value, JSModuleEnvironment* environment, ScopeOffset scopeOffset)
+    {
+        setValue(slotBase, attributes, value);
+        m_additionalDataType = AdditionalDataType::ModuleNamespace;
+        m_additionalData.moduleNamespaceSlot.environment = environment;
+        m_additionalData.moduleNamespaceSlot.scopeOffset = scopeOffset.offset();
+    }
+
     void setCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue)
     {
         ASSERT(attributes == attributesForStructure(attributes));
@@ -220,7 +251,7 @@ public:
         m_offset = invalidOffset;
     }
     
-    void setCacheableCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue, DOMJIT::GetterSetter* domJIT = nullptr)
+    void setCacheableCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue)
     {
         ASSERT(attributes == attributesForStructure(attributes));
         
@@ -232,7 +263,15 @@ public:
         m_slotBase = slotBase;
         m_propertyType = TypeCustom;
         m_offset = !invalidOffset;
-        m_domJIT = domJIT;
+    }
+
+    void setCacheableCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue, DOMJIT::GetterSetter* domJIT)
+    {
+        setCacheableCustom(slotBase, attributes, getValue);
+        if (domJIT) {
+            m_additionalDataType = AdditionalDataType::DOMJIT;
+            m_additionalData.domJIT = domJIT;
+        }
     }
 
     void setCustomGetterSetter(JSObject* slotBase, unsigned attributes, CustomGetterSetter* getterSetter)
@@ -328,7 +367,11 @@ private:
     CacheabilityType m_cacheability;
     PropertyType m_propertyType;
     InternalMethodType m_internalMethodType;
-    DOMJIT::GetterSetter* m_domJIT { nullptr };
+    AdditionalDataType m_additionalDataType;
+    union {
+        DOMJIT::GetterSetter* domJIT;
+        ModuleNamespaceSlot moduleNamespaceSlot;
+    } m_additionalData;
     bool m_isTaintedByOpaqueObject;
 };