We don't do context switches for Wasm->Wasm call indirect
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 18 May 2017 00:23:56 +0000 (00:23 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 18 May 2017 00:23:56 +0000 (00:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=172188
<rdar://problem/32231828>

Reviewed by Keith Miller.

JSTests:

* wasm/function-tests/context-switch.js: Added.
(import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.makeInstance):
(import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.makeInstance2):
(import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.const.i2.makeInstance2):
(makeInstance):
(makeInstance2):
(assert.eq.makeInstance):
(assert.eq.makeInstance2):
(assert.eq):

Source/JavaScriptCore:

We did not do a context switch when doing an indirect call.
This is clearly wrong, since the thing we're making an indirect
call to could be from another instance. This patch fixes this
oversight by doing a very simple context switch. I've also opened
a bug to make indirect calls fast: https://bugs.webkit.org/show_bug.cgi?id=172197
since this patch adds yet another branch to the indirect call path.
I've also added tests that either throw or crash before this change.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* wasm/WasmB3IRGenerator.cpp:
* wasm/js/JSWebAssemblyTable.h:
(JSC::JSWebAssemblyTable::offsetOfJSFunctions):
* wasm/js/WebAssemblyFunction.cpp:
(JSC::WebAssemblyFunction::visitChildren):
(JSC::WebAssemblyFunction::finishCreation): Deleted.
* wasm/js/WebAssemblyFunction.h:
(JSC::WebAssemblyFunction::instance): Deleted.
(JSC::WebAssemblyFunction::offsetOfInstance): Deleted.
* wasm/js/WebAssemblyFunctionBase.cpp: Added.
(JSC::WebAssemblyFunctionBase::WebAssemblyFunctionBase):
(JSC::WebAssemblyFunctionBase::visitChildren):
(JSC::WebAssemblyFunctionBase::finishCreation):
* wasm/js/WebAssemblyFunctionBase.h: Added.
(JSC::WebAssemblyFunctionBase::instance):
(JSC::WebAssemblyFunctionBase::offsetOfInstance):
* wasm/js/WebAssemblyModuleRecord.cpp:
(JSC::WebAssemblyModuleRecord::link):
(JSC::WebAssemblyModuleRecord::evaluate):
* wasm/js/WebAssemblyWrapperFunction.cpp:
(JSC::WebAssemblyWrapperFunction::create):
(JSC::WebAssemblyWrapperFunction::finishCreation):
(JSC::WebAssemblyWrapperFunction::visitChildren):
* wasm/js/WebAssemblyWrapperFunction.h:

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

14 files changed:
JSTests/ChangeLog
JSTests/wasm/function-tests/context-switch.js [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.h
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.h
Source/JavaScriptCore/wasm/js/WebAssemblyFunctionBase.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/js/WebAssemblyFunctionBase.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyWrapperFunction.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyWrapperFunction.h

index 3976823..1e056d9 100644 (file)
@@ -1,3 +1,21 @@
+2017-05-17  Saam Barati  <sbarati@apple.com>
+
+        We don't do context switches for Wasm->Wasm call indirect
+        https://bugs.webkit.org/show_bug.cgi?id=172188
+        <rdar://problem/32231828>
+
+        Reviewed by Keith Miller.
+
+        * wasm/function-tests/context-switch.js: Added.
+        (import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.makeInstance):
+        (import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.makeInstance2):
+        (import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.const.i2.makeInstance2):
+        (makeInstance):
+        (makeInstance2):
+        (assert.eq.makeInstance):
+        (assert.eq.makeInstance2):
+        (assert.eq):
+
 2017-05-17  Filip Pizlo  <fpizlo@apple.com>
 
         JSC: Incorrect LoadVarargs handling in ArgumentsEliminationPhase::transform
diff --git a/JSTests/wasm/function-tests/context-switch.js b/JSTests/wasm/function-tests/context-switch.js
new file mode 100644 (file)
index 0000000..0595e6d
--- /dev/null
@@ -0,0 +1,277 @@
+import Builder from '../Builder.js'
+import * as assert from '../assert.js'
+
+{
+    function makeInstance() {
+        const tableDescription = {initial: 1, element: "anyfunc"};
+        const builder = new Builder()
+            .Type()
+                .Func([], "void")
+            .End()
+            .Import()
+                .Table("imp", "table", tableDescription)
+            .End()
+            .Function().End()
+            .Export()
+                .Function("foo")
+            .End()
+            .Code()
+                .Function("foo", {params:["i32"], ret:"void"})
+                    .GetLocal(0) // parameter to call
+                    .GetLocal(0) // call index
+                    .CallIndirect(0, 0) // calling function of type [] => 'void'
+                    .Return()
+                .End()
+            .End();
+
+
+        const bin = builder.WebAssembly().get();
+        const module = new WebAssembly.Module(bin);
+        const table = new WebAssembly.Table(tableDescription);
+        return {instance: new WebAssembly.Instance(module, {imp: {table}}), table};
+    }
+
+    function makeInstance2(f) {
+        const builder = new Builder()
+            .Type()
+            .End()
+            .Import()
+                .Function("imp", "f", {params:[], ret:"void"})
+            .End()
+            .Function().End()
+            .Export()
+                .Function("foo")
+            .End()
+            .Code()
+                .Function("foo", {params: [], ret: "void" })
+                    .Call(0)
+                    .Return()
+                .End()
+            .End();
+
+
+        const bin = builder.WebAssembly().get();
+        const module = new WebAssembly.Module(bin);
+        return new WebAssembly.Instance(module, {imp: {f}});
+    }
+
+    const {instance: i1, table: t1} = makeInstance();
+    const foo = i1.exports.foo;
+
+    let called = false;
+    let shouldThrow = false;
+    const i2 = makeInstance2(function() {
+        called = true;
+        if (shouldThrow)
+            throw new Error("Threw");
+    });
+
+    t1.set(0, i2.exports.foo);
+    for (let i = 0; i < 1000; ++i) {
+        foo(0);
+        assert.eq(called, true);
+        called = false;
+    }
+    shouldThrow = true;
+    for (let i = 0; i < 1000; ++i) {
+        assert.throws(() => foo(0), Error, "Threw");
+        assert.eq(called, true);
+        called = false;
+    }
+}
+
+{
+    function makeInstance() {
+        const tableDescription = {initial: 1, element: "anyfunc"};
+        const builder = new Builder()
+            .Type()
+                .Func(["i32"], "void")
+            .End()
+            .Import()
+                .Table("imp", "table", tableDescription)
+            .End()
+            .Function().End()
+            .Export()
+                .Function("foo")
+            .End()
+            .Code()
+                .Function("foo", {params:["i32", "i32"], ret:"void"})
+                    .GetLocal(1) // parameter to call
+                    .GetLocal(0) // call index
+                    .CallIndirect(0, 0) // calling function of type ['i32'] => 'void'
+                    .Return()
+                .End()
+            .End();
+
+        const bin = builder.WebAssembly().get();
+        const module = new WebAssembly.Module(bin);
+        const table = new WebAssembly.Table(tableDescription);
+        return {instance: new WebAssembly.Instance(module, {imp: {table}}), table};
+    }
+
+    function makeInstance2(memory, f) {
+        const builder = new Builder()
+            .Type()
+            .End()
+            .Import()
+                .Function("imp", "f", {params:['i32', 'i32'], ret:"void"})
+                .Memory("imp", "memory", memoryDescription)
+            .End()
+            .Function().End()
+            .Export()
+                .Function("foo")
+            .End()
+            .Code()
+                .Function("foo", {params: ["i32"], ret: "void" })
+                    .GetLocal(0)
+                    .GetLocal(0)
+                    .I32Load(2, 0)
+                    .Call(0)
+                    .Return()
+                .End()
+            .End();
+
+
+        const bin = builder.WebAssembly().get();
+        const module = new WebAssembly.Module(bin);
+        return new WebAssembly.Instance(module, {imp: {f, memory}});
+    }
+
+    const {instance: i1, table: t1} = makeInstance();
+    const foo = (memOffset) => i1.exports.foo(0, memOffset);
+
+    const memoryDescription = {initial:1};
+    const mem = new WebAssembly.Memory(memoryDescription);
+    let called = false;
+
+    const pageSize = 64 * 1024;
+    let buf = new Uint32Array(mem.buffer);
+    const i2 = makeInstance2(mem, function(i, value) {
+        assert.eq(i & 3, 0);
+        assert.eq(buf[i/4], value);
+        called = true;
+    });
+    t1.set(0, i2.exports.foo);
+    
+    for (let i = 0; i < pageSize/4; ++i) {
+        buf[i] = i+1;
+    }
+
+    for (let i = 0; i < pageSize/4; ++i) {
+        foo(i*4);
+        assert.eq(called, true);
+        called = false;
+    }
+
+    for (let i = pageSize/4; i < 2*pageSize/4; ++i) {
+        assert.throws(() => foo(i*4), WebAssembly.RuntimeError, "Out of bounds memory access");
+        assert.eq(called, false);
+    }
+}
+
+{
+    function makeInstance() {
+        const tableDescription = {initial: 1, element: "anyfunc"};
+        const builder = new Builder()
+            .Type()
+                .Func(["i32"], "void")
+            .End()
+            .Import()
+                .Table("imp", "table", tableDescription)
+            .End()
+            .Function().End()
+            .Export()
+                .Function("foo")
+            .End()
+            .Code()
+                .Function("foo", {params:["i32", "i32"], ret:"void"})
+                    .GetLocal(1) // parameter to call
+                    .GetLocal(0) // call index
+                    .CallIndirect(0, 0) // calling function of type ['i32'] => 'void'
+                    .Return()
+                .End()
+            .End();
+
+        const bin = builder.WebAssembly().get();
+        const module = new WebAssembly.Module(bin);
+        const table = new WebAssembly.Table(tableDescription);
+        return {instance: new WebAssembly.Instance(module, {imp: {table}}), table};
+    }
+
+    function makeInstance2(memory, f) {
+        const builder = new Builder()
+            .Type()
+            .End()
+            .Import()
+                .Function("imp", "f", {params:['i32', 'i32'], ret:"void"})
+                .Memory("imp", "memory", memoryDescription)
+            .End()
+            .Function().End()
+            .Export()
+                .Function("foo")
+            .End()
+            .Code()
+                .Function("foo", {params: ["i32"], ret: "void" })
+                    .GetLocal(0)
+                    .GetLocal(0)
+                    .I32Load(2, 0)
+                    .Call(0)
+                    .Return()
+                .End()
+            .End();
+
+
+        const bin = builder.WebAssembly().get();
+        const module = new WebAssembly.Module(bin);
+        return new WebAssembly.Instance(module, {imp: {f, memory}});
+    }
+
+    function exportImport(f) {
+        let builder = (new Builder())
+            .Type().End()
+            .Import()
+                .Function("imp", "f", {params: ['i32'], ret:"void"})
+            .End()
+            .Function().End()
+            .Export()
+                .Function("func", {module: "imp", field: "f"})
+            .End()
+            .Code().End();
+        return (new WebAssembly.Instance(new WebAssembly.Module(builder.WebAssembly().get()), {imp: {f}})).exports.func;
+    }
+
+    const {instance: i1, table: t1} = makeInstance();
+    const foo = (memOffset) => i1.exports.foo(0, memOffset);
+
+    const memoryDescription = {initial:1};
+    const mem = new WebAssembly.Memory(memoryDescription);
+    let called = false;
+
+    const pageSize = 64 * 1024;
+    let buf = new Uint32Array(mem.buffer);
+    const i2 = makeInstance2(mem, function(i, value) {
+        assert.eq(i & 3, 0);
+        assert.eq(buf[i/4], value);
+        called = true;
+    });
+
+    const exportedImport = exportImport(function(offset) {
+        i2.exports.foo(offset);
+    });
+    t1.set(0, exportedImport);
+    
+    for (let i = 0; i < pageSize/4; ++i) {
+        buf[i] = i+1;
+    }
+
+    for (let i = 0; i < pageSize/4; ++i) {
+        foo(i*4);
+        assert.eq(called, true);
+        called = false;
+    }
+
+    for (let i = pageSize/4; i < 2*pageSize/4; ++i) {
+        assert.throws(() => foo(i*4), WebAssembly.RuntimeError, "Out of bounds memory access");
+        assert.eq(called, false);
+    }
+}
index 50f03b1..31d7ca3 100644 (file)
@@ -981,6 +981,7 @@ set(JavaScriptCore_SOURCES
     wasm/js/WebAssemblyCompileErrorConstructor.cpp
     wasm/js/WebAssemblyCompileErrorPrototype.cpp
     wasm/js/WebAssemblyFunction.cpp
+    wasm/js/WebAssemblyFunctionBase.cpp
     wasm/js/WebAssemblyInstanceConstructor.cpp
     wasm/js/WebAssemblyInstancePrototype.cpp
     wasm/js/WebAssemblyLinkErrorConstructor.cpp
index 285444f..7f0b54c 100644 (file)
@@ -1,3 +1,46 @@
+2017-05-17  Saam Barati  <sbarati@apple.com>
+
+        We don't do context switches for Wasm->Wasm call indirect
+        https://bugs.webkit.org/show_bug.cgi?id=172188
+        <rdar://problem/32231828>
+
+        Reviewed by Keith Miller.
+
+        We did not do a context switch when doing an indirect call. 
+        This is clearly wrong, since the thing we're making an indirect
+        call to could be from another instance. This patch fixes this
+        oversight by doing a very simple context switch. I've also opened
+        a bug to make indirect calls fast: https://bugs.webkit.org/show_bug.cgi?id=172197
+        since this patch adds yet another branch to the indirect call path.
+        I've also added tests that either throw or crash before this change.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * wasm/WasmB3IRGenerator.cpp:
+        * wasm/js/JSWebAssemblyTable.h:
+        (JSC::JSWebAssemblyTable::offsetOfJSFunctions):
+        * wasm/js/WebAssemblyFunction.cpp:
+        (JSC::WebAssemblyFunction::visitChildren):
+        (JSC::WebAssemblyFunction::finishCreation): Deleted.
+        * wasm/js/WebAssemblyFunction.h:
+        (JSC::WebAssemblyFunction::instance): Deleted.
+        (JSC::WebAssemblyFunction::offsetOfInstance): Deleted.
+        * wasm/js/WebAssemblyFunctionBase.cpp: Added.
+        (JSC::WebAssemblyFunctionBase::WebAssemblyFunctionBase):
+        (JSC::WebAssemblyFunctionBase::visitChildren):
+        (JSC::WebAssemblyFunctionBase::finishCreation):
+        * wasm/js/WebAssemblyFunctionBase.h: Added.
+        (JSC::WebAssemblyFunctionBase::instance):
+        (JSC::WebAssemblyFunctionBase::offsetOfInstance):
+        * wasm/js/WebAssemblyModuleRecord.cpp:
+        (JSC::WebAssemblyModuleRecord::link):
+        (JSC::WebAssemblyModuleRecord::evaluate):
+        * wasm/js/WebAssemblyWrapperFunction.cpp:
+        (JSC::WebAssemblyWrapperFunction::create):
+        (JSC::WebAssemblyWrapperFunction::finishCreation):
+        (JSC::WebAssemblyWrapperFunction::visitChildren):
+        * wasm/js/WebAssemblyWrapperFunction.h:
+
 2017-05-17  Filip Pizlo  <fpizlo@apple.com>
 
         JSC: Incorrect LoadVarargs handling in ArgumentsEliminationPhase::transform
index 19aa6e7..24af7af 100644 (file)
                4443AE3316E188D90076F110 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51F0EB6105C86C6B00E6DF1B /* Foundation.framework */; };
                451539B912DC994500EF7AC4 /* Yarr.h in Headers */ = {isa = PBXBuildFile; fileRef = 451539B812DC994500EF7AC4 /* Yarr.h */; settings = {ATTRIBUTES = (Private, ); }; };
                473DA4A4764C45FE871B0485 /* DefinePropertyAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 169948EDE68D4054B01EF797 /* DefinePropertyAttributes.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               521322451ECBCE8200F65615 /* WebAssemblyFunctionBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 521322431ECBCE8200F65615 /* WebAssemblyFunctionBase.cpp */; };
+               521322461ECBCE8200F65615 /* WebAssemblyFunctionBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 521322441ECBCE8200F65615 /* WebAssemblyFunctionBase.h */; };
                5250D2D11E8DA05A0029A932 /* WasmThunks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5250D2CF1E8DA05A0029A932 /* WasmThunks.cpp */; };
                5250D2D21E8DA05A0029A932 /* WasmThunks.h in Headers */ = {isa = PBXBuildFile; fileRef = 5250D2D01E8DA05A0029A932 /* WasmThunks.h */; settings = {ATTRIBUTES = (Private, ); }; };
                525C0DD91E935847002184CD /* WasmCallee.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 525C0DD71E935847002184CD /* WasmCallee.cpp */; };
                52C952B719A289850069B386 /* TypeProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 52C952B619A289850069B386 /* TypeProfiler.h */; settings = {ATTRIBUTES = (Private, ); }; };
                52C952B919A28A1C0069B386 /* TypeProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52C952B819A28A1C0069B386 /* TypeProfiler.cpp */; };
                52F6C35D1E71EB080081F4CC /* WebAssemblyWrapperFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52F6C35B1E71EB080081F4CC /* WebAssemblyWrapperFunction.cpp */; };
-               52F6C35E1E71EB080081F4CC /* WebAssemblyWrapperFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 52F6C35C1E71EB080081F4CC /* WebAssemblyWrapperFunction.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               52F6C35E1E71EB080081F4CC /* WebAssemblyWrapperFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 52F6C35C1E71EB080081F4CC /* WebAssemblyWrapperFunction.h */; };
                530FB3021E7A0B6E003C19DD /* WasmWorklist.h in Headers */ = {isa = PBXBuildFile; fileRef = 530FB3011E7A0B6E003C19DD /* WasmWorklist.h */; };
                530FB3041E7A1146003C19DD /* WasmWorklist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 530FB3031E7A1146003C19DD /* WasmWorklist.cpp */; };
                5311BD4A1EA581E500525281 /* WasmOMGPlan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5311BD481EA581E500525281 /* WasmOMGPlan.cpp */; };
                4CE978E385A8498199052153 /* ModuleNamespaceAccessCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModuleNamespaceAccessCase.h; sourceTree = "<group>"; };
                51F0EB6105C86C6B00E6DF1B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
                51F0EC0705C86C9A00E6DF1B /* libobjc.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libobjc.dylib; path = /usr/lib/libobjc.dylib; sourceTree = "<absolute>"; };
+               521322431ECBCE8200F65615 /* WebAssemblyFunctionBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebAssemblyFunctionBase.cpp; path = js/WebAssemblyFunctionBase.cpp; sourceTree = "<group>"; };
+               521322441ECBCE8200F65615 /* WebAssemblyFunctionBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebAssemblyFunctionBase.h; path = js/WebAssemblyFunctionBase.h; sourceTree = "<group>"; };
                5250D2CF1E8DA05A0029A932 /* WasmThunks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmThunks.cpp; sourceTree = "<group>"; };
                5250D2D01E8DA05A0029A932 /* WasmThunks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmThunks.h; sourceTree = "<group>"; };
                525C0DD71E935847002184CD /* WasmCallee.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmCallee.cpp; sourceTree = "<group>"; };
                                AD2FCBB31DB58DA400B3E736 /* WebAssemblyCompileErrorPrototype.h */,
                                AD4937C91DDD27340077C807 /* WebAssemblyFunction.cpp */,
                                AD4937CA1DDD27340077C807 /* WebAssemblyFunction.h */,
+                               521322431ECBCE8200F65615 /* WebAssemblyFunctionBase.cpp */,
+                               521322441ECBCE8200F65615 /* WebAssemblyFunctionBase.h */,
                                AD2FCBB41DB58DA400B3E736 /* WebAssemblyInstanceConstructor.cpp */,
                                AD2FCBB51DB58DA400B3E736 /* WebAssemblyInstanceConstructor.h */,
                                AD2FCBB61DB58DA400B3E736 /* WebAssemblyInstancePrototype.cpp */,
                                0F34B14A16D42013001CDA5A /* DFGUseKind.h in Headers */,
                                0F9715311EB28BEE00A1645D /* GCRequest.h in Headers */,
                                0F3B3A2C15475002003ED0FF /* DFGValidate.h in Headers */,
+                               521322461ECBCE8200F65615 /* WebAssemblyFunctionBase.h in Headers */,
                                0F2BDC481522802900CD8910 /* DFGValueSource.h in Headers */,
                                0F0123331944EA1B00843A0C /* DFGValueStrength.h in Headers */,
                                0FE254F71ABDDD2200A7C6D2 /* DFGVarargsForwardingPhase.h in Headers */,
                                A5398FAC1C750DA60060A963 /* HeapProfiler.cpp in Sources */,
                                A54C2AB01C6544EE00A18D78 /* HeapSnapshot.cpp in Sources */,
                                A5311C371C77CECA00E6B1B6 /* HeapSnapshotBuilder.cpp in Sources */,
+                               521322451ECBCE8200F65615 /* WebAssemblyFunctionBase.cpp in Sources */,
                                0F4680D414BBD24900BFE272 /* HostCallReturnValue.cpp in Sources */,
                                DC2143081CA32E58000A8869 /* ICStats.cpp in Sources */,
                                147F39CE107EC37600427A48 /* Identifier.cpp in Sources */,
index 4c67122..9f3d8d4 100644 (file)
@@ -1089,12 +1089,15 @@ auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<Expressio
     ASSERT(signature.argumentCount() == args.size());
 
     ExpressionType callableFunctionBuffer;
+    ExpressionType jsFunctionBuffer;
     ExpressionType callableFunctionBufferSize;
     {
         ExpressionType table = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
             m_instanceValue, safeCast<int32_t>(JSWebAssemblyInstance::offsetOfTable()));
         callableFunctionBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
             table, safeCast<int32_t>(JSWebAssemblyTable::offsetOfFunctions()));
+        jsFunctionBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
+            table, safeCast<int32_t>(JSWebAssemblyTable::offsetOfJSFunctions()));
         callableFunctionBufferSize = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin(),
             table, safeCast<int32_t>(JSWebAssemblyTable::offsetOfSize()));
     }
@@ -1140,11 +1143,57 @@ auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<Expressio
         });
     }
 
+    // Do a context switch if needed.
+    {
+        Value* offset = m_currentBlock->appendNew<Value>(m_proc, Mul, origin(),
+            m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin(), calleeIndex),
+            constant(pointerType(), sizeof(WriteBarrier<JSObject>)));
+        Value* jsObject = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
+            m_currentBlock->appendNew<Value>(m_proc, Add, origin(), jsFunctionBuffer, offset));
+
+        BasicBlock* continuation = m_proc.addBlock();
+        BasicBlock* doContextSwitch = m_proc.addBlock();
+
+        Value* newContext = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
+            jsObject, safeCast<int32_t>(WebAssemblyFunctionBase::offsetOfInstance()));
+        Value* isSameContext = m_currentBlock->appendNew<Value>(m_proc, Equal, origin(),
+            newContext, m_instanceValue);
+        m_currentBlock->appendNewControlValue(m_proc, B3::Branch, origin(),
+            isSameContext, FrequentedBlock(continuation), FrequentedBlock(doContextSwitch));
+
+        PatchpointValue* patchpoint = doContextSwitch->appendNew<PatchpointValue>(m_proc, B3::Void, origin());
+        patchpoint->effects.writesPinned = true;
+        // We pessimistically assume we're calling something with BoundsChecking memory.
+        // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
+        patchpoint->clobber(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
+        patchpoint->clobber(RegisterSet::macroScratchRegisters());
+        patchpoint->append(newContext, ValueRep::SomeRegister);
+        patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
+            AllowMacroScratchRegisterUsage allowScratch(jit);
+            GPRReg newContext = params[0].gpr();
+            const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
+            const auto& sizeRegs = pinnedRegs.sizeRegisters;
+            GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
+            ASSERT(newContext != baseMemory);
+
+            jit.storeWasmContext(newContext);
+            jit.loadPtr(CCallHelpers::Address(newContext, Context::offsetOfMemory()), baseMemory); // JSWebAssemblyMemory*.
+            ASSERT(sizeRegs.size() == 1);
+            ASSERT(sizeRegs[0].sizeRegister != baseMemory);
+            ASSERT(sizeRegs[0].sizeRegister != newContext);
+            ASSERT(!sizeRegs[0].sizeOffset);
+            jit.loadPtr(CCallHelpers::Address(baseMemory, JSWebAssemblyMemory::offsetOfSize()), sizeRegs[0].sizeRegister); // Memory size.
+            jit.loadPtr(CCallHelpers::Address(baseMemory, JSWebAssemblyMemory::offsetOfMemory()), baseMemory); // WasmMemory::void*.
+        });
+        doContextSwitch->appendNewControlValue(m_proc, Jump, origin(), continuation);
+
+        m_currentBlock = continuation;
+    }
+
     ExpressionType calleeCode = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
         m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), callableFunction,
             safeCast<int32_t>(OBJECT_OFFSETOF(CallableFunction, code))));
 
-
     Type returnType = signature.returnType();
     result = wasmCallingConvention().setupCall(m_proc, m_currentBlock, origin(), args, toB3Type(returnType),
         [=] (PatchpointValue* patchpoint) {
index 58723ab..b7a4136 100644 (file)
@@ -62,6 +62,7 @@ public:
 
     static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(JSWebAssemblyTable, m_size); }
     static ptrdiff_t offsetOfFunctions() { return OBJECT_OFFSETOF(JSWebAssemblyTable, m_functions); }
+    static ptrdiff_t offsetOfJSFunctions() { return OBJECT_OFFSETOF(JSWebAssemblyTable, m_jsFunctions); }
 
     static bool isValidSize(uint32_t size)
     {
index 8a7a1d0..e2800bf 100644 (file)
@@ -184,14 +184,6 @@ void WebAssemblyFunction::visitChildren(JSCell* cell, SlotVisitor& visitor)
     WebAssemblyFunction* thisObject = jsCast<WebAssemblyFunction*>(cell);
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     Base::visitChildren(thisObject, visitor);
-    visitor.append(thisObject->m_instance);
-}
-
-void WebAssemblyFunction::finishCreation(VM& vm, NativeExecutable* executable, unsigned length, const String& name, JSWebAssemblyInstance* instance)
-{
-    Base::finishCreation(vm, executable, length, name);
-    ASSERT(inherits(vm, info()));
-    m_instance.set(vm, this, instance);
 }
 
 } // namespace JSC
index 3bba093..5ac522b 100644 (file)
@@ -27,8 +27,8 @@
 
 #if ENABLE(WEBASSEMBLY)
 
-#include "JSFunction.h"
 #include "WasmCallee.h"
+#include "WebAssemblyFunctionBase.h"
 #include <wtf/Noncopyable.h>
 
 namespace JSC {
@@ -41,9 +41,9 @@ namespace B3 {
 class Compilation;
 }
 
-class WebAssemblyFunction : public JSFunction {
+class WebAssemblyFunction : public WebAssemblyFunctionBase {
 public:
-    typedef JSFunction Base;
+    using Base = WebAssemblyFunctionBase;
 
     const static unsigned StructureFlags = Base::StructureFlags;
 
@@ -52,25 +52,20 @@ public:
     JS_EXPORT_PRIVATE static WebAssemblyFunction* create(VM&, JSGlobalObject*, unsigned, const String&, JSWebAssemblyInstance*, Wasm::Callee& jsEntrypoint, Wasm::WasmEntrypointLoadLocation, Wasm::SignatureIndex);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
-    JSWebAssemblyInstance* instance() const { return m_instance.get(); }
     Wasm::SignatureIndex signatureIndex() const { return m_wasmFunction.signatureIndex; }
     Wasm::WasmEntrypointLoadLocation wasmEntrypointLoadLocation() const { return m_wasmFunction.code; }
     Wasm::CallableFunction callableFunction() const { return m_wasmFunction; }
 
     void* jsEntrypoint() { return m_jsEntrypoint; }
 
-    static ptrdiff_t offsetOfInstance() { return OBJECT_OFFSETOF(WebAssemblyFunction, m_instance); }
     static ptrdiff_t offsetOfWasmEntrypointLoadLocation() { return OBJECT_OFFSETOF(WebAssemblyFunction, m_wasmFunction) + Wasm::CallableFunction::offsetOfWasmEntrypointLoadLocation(); }
 
 protected:
     static void visitChildren(JSCell*, SlotVisitor&);
 
-    void finishCreation(VM&, NativeExecutable*, unsigned length, const String& name, JSWebAssemblyInstance*);
-
 private:
     WebAssemblyFunction(VM&, JSGlobalObject*, Structure*, Wasm::Callee& jsEntrypoint, Wasm::WasmEntrypointLoadLocation, Wasm::SignatureIndex);
 
-    WriteBarrier<JSWebAssemblyInstance> m_instance;
     // It's safe to just hold the raw CallableFunction/jsEntrypoint because we have a reference
     // to our Instance, which points to the Module that exported us, which
     // ensures that the actual Signature/code doesn't get deallocated.
diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionBase.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionBase.cpp
new file mode 100644 (file)
index 0000000..79e934d
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WebAssemblyFunctionBase.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "JSCInlines.h"
+#include "JSWebAssemblyInstance.h"
+
+namespace JSC {
+
+const ClassInfo WebAssemblyFunctionBase::s_info = { "WebAssemblyFunctionBase", &Base::s_info, nullptr, CREATE_METHOD_TABLE(WebAssemblyFunctionBase) };
+
+WebAssemblyFunctionBase::WebAssemblyFunctionBase(VM& vm, JSGlobalObject* globalObject, Structure* structure)
+    : Base(vm, globalObject, structure)
+{ }
+
+void WebAssemblyFunctionBase::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+    WebAssemblyFunctionBase* thisObject = jsCast<WebAssemblyFunctionBase*>(cell);
+    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+    Base::visitChildren(thisObject, visitor);
+    visitor.append(thisObject->m_instance);
+}
+
+void WebAssemblyFunctionBase::finishCreation(VM& vm, NativeExecutable* executable, unsigned length, const String& name, JSWebAssemblyInstance* instance)
+{
+    Base::finishCreation(vm, executable, length, name);
+    ASSERT(inherits(vm, info()));
+    m_instance.set(vm, this, instance);
+}
+
+} // namespace JSC
+
+#endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionBase.h b/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionBase.h
new file mode 100644 (file)
index 0000000..4973e4d
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "JSFunction.h"
+
+namespace JSC {
+
+class JSGlobalObject;
+class JSWebAssemblyInstance;
+
+class WebAssemblyFunctionBase : public JSFunction {
+public:
+    using Base = JSFunction;
+
+    const static unsigned StructureFlags = Base::StructureFlags;
+
+    DECLARE_INFO;
+
+    JSWebAssemblyInstance* instance() const { return m_instance.get(); }
+    static ptrdiff_t offsetOfInstance() { return OBJECT_OFFSETOF(WebAssemblyFunctionBase, m_instance); }
+
+protected:
+    static void visitChildren(JSCell*, SlotVisitor&);
+    void finishCreation(VM&, NativeExecutable*, unsigned length, const String& name, JSWebAssemblyInstance*);
+    WebAssemblyFunctionBase(VM&, JSGlobalObject*, Structure*);
+    WriteBarrier<JSWebAssemblyInstance> m_instance;
+};
+
+} // namespace JSC
+
+#endif // ENABLE(WEBASSEMBLY)
index 7054a89..c935967 100644 (file)
@@ -117,7 +117,7 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSWebAssemblyModule* module,
                     exportedValue = functionImport;
                 else {
                     Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(functionIndex);
-                    exportedValue = WebAssemblyWrapperFunction::create(vm, globalObject, functionImport, functionIndex, codeBlock, signatureIndex);
+                    exportedValue = WebAssemblyWrapperFunction::create(vm, globalObject, functionImport, functionIndex, instance, signatureIndex);
                 }
             } else {
                 //   iii. Otherwise:
@@ -264,7 +264,7 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec)
                     }
 
                     table->setFunction(vm, tableIndex,
-                        WebAssemblyWrapperFunction::create(vm, m_instance->globalObject(), functionImport, functionIndex, codeBlock, signatureIndex));
+                        WebAssemblyWrapperFunction::create(vm, m_instance->globalObject(), functionImport, functionIndex, m_instance.get(), signatureIndex));
                     ++tableIndex;
                     continue;
                 }
index b20b54b..e8e43a5 100644 (file)
@@ -31,6 +31,7 @@
 #include "Error.h"
 #include "FunctionPrototype.h"
 #include "JSCInlines.h"
+#include "JSWebAssemblyInstance.h"
 
 namespace JSC {
 
@@ -57,23 +58,22 @@ WebAssemblyWrapperFunction::WebAssemblyWrapperFunction(VM& vm, JSGlobalObject* g
     , m_wasmFunction(wasmFunction)
 { }
 
-WebAssemblyWrapperFunction* WebAssemblyWrapperFunction::create(VM& vm, JSGlobalObject* globalObject, JSObject* function, unsigned importIndex, JSWebAssemblyCodeBlock* codeBlock, Wasm::SignatureIndex signatureIndex)
+WebAssemblyWrapperFunction* WebAssemblyWrapperFunction::create(VM& vm, JSGlobalObject* globalObject, JSObject* function, unsigned importIndex, JSWebAssemblyInstance* instance, Wasm::SignatureIndex signatureIndex)
 {
     ASSERT_WITH_MESSAGE(!function->inherits(vm, WebAssemblyWrapperFunction::info()), "We should never double wrap a wrapper function.");
     String name = "";
     NativeExecutable* executable = vm.getHostFunction(callWebAssemblyWrapperFunction, NoIntrinsic, callHostFunctionAsConstructor, nullptr, name);
-    WebAssemblyWrapperFunction* result = new (NotNull, allocateCell<WebAssemblyWrapperFunction>(vm.heap)) WebAssemblyWrapperFunction(vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), Wasm::CallableFunction(signatureIndex, codeBlock->wasmToJsCallStubForImport(importIndex)));
+    WebAssemblyWrapperFunction* result = new (NotNull, allocateCell<WebAssemblyWrapperFunction>(vm.heap)) WebAssemblyWrapperFunction(vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), Wasm::CallableFunction(signatureIndex, instance->codeBlock()->wasmToJsCallStubForImport(importIndex)));
     const Wasm::Signature& signature = Wasm::SignatureInformation::get(signatureIndex);
-    result->finishCreation(vm, executable, signature.argumentCount(), name, function, codeBlock);
+    result->finishCreation(vm, executable, signature.argumentCount(), name, function, instance);
     return result;
 }
 
-void WebAssemblyWrapperFunction::finishCreation(VM& vm, NativeExecutable* executable, unsigned length, const String& name, JSObject* function, JSWebAssemblyCodeBlock* codeBlock)
+void WebAssemblyWrapperFunction::finishCreation(VM& vm, NativeExecutable* executable, unsigned length, const String& name, JSObject* function, JSWebAssemblyInstance* instance)
 {
-    Base::finishCreation(vm, executable, length, name);
+    Base::finishCreation(vm, executable, length, name, instance);
     RELEASE_ASSERT(JSValue(function).isFunction());
     m_function.set(vm, this, function);
-    m_codeBlock.set(vm, this, codeBlock);
 }
 
 Structure* WebAssemblyWrapperFunction::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
@@ -88,7 +88,6 @@ void WebAssemblyWrapperFunction::visitChildren(JSCell* cell, SlotVisitor& visito
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     Base::visitChildren(thisObject, visitor);
 
-    visitor.append(thisObject->m_codeBlock);
     visitor.append(thisObject->m_function);
 }
 
index 9f6d346..5c6c677 100644 (file)
 
 #if ENABLE(WEBASSEMBLY)
 
-#include "JSFunction.h"
 #include "JSWebAssemblyCodeBlock.h"
+#include "WebAssemblyFunctionBase.h"
 
 namespace JSC {
 
-class WebAssemblyWrapperFunction : public JSFunction {
+class WebAssemblyWrapperFunction : public WebAssemblyFunctionBase {
 public:
-    typedef JSFunction Base;
+    using Base = WebAssemblyFunctionBase;
 
     const static unsigned StructureFlags = Base::StructureFlags;
 
     DECLARE_INFO;
 
-    static WebAssemblyWrapperFunction* create(VM&, JSGlobalObject*, JSObject*, unsigned importIndex, JSWebAssemblyCodeBlock*, Wasm::SignatureIndex);
+    static WebAssemblyWrapperFunction* create(VM&, JSGlobalObject*, JSObject*, unsigned importIndex, JSWebAssemblyInstance*, Wasm::SignatureIndex);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     Wasm::SignatureIndex signatureIndex() const { return m_wasmFunction.signatureIndex; }
@@ -51,18 +51,15 @@ public:
 protected:
     static void visitChildren(JSCell*, SlotVisitor&);
 
-    void finishCreation(VM&, NativeExecutable*, unsigned length, const String& name, JSObject*, JSWebAssemblyCodeBlock*);
+    void finishCreation(VM&, NativeExecutable*, unsigned length, const String& name, JSObject*, JSWebAssemblyInstance*);
 
 private:
     WebAssemblyWrapperFunction(VM&, JSGlobalObject*, Structure*, Wasm::CallableFunction);
 
-    // We keep a reference to our CodeBlock because we have a raw
-    // pointer to asm code that it owns.
-    WriteBarrier<JSWebAssemblyCodeBlock> m_codeBlock;
     WriteBarrier<JSObject> m_function;
     // It's safe to just hold the raw CallableFunction because we have a reference
-    // to our CodeBlock, which points to the Module that exported us, which
-    // ensures that the actual Signature/code doesn't get deallocated.
+    // to our Instance, which points to the CodeBlock, which points to the Module
+    // that exported us, which ensures that the actual Signature/code doesn't get deallocated.
     Wasm::CallableFunction m_wasmFunction;
 };