WebAssembly: handle and optimize wasm export → wasm import calls
authorjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Jan 2017 01:57:40 +0000 (01:57 +0000)
committerjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Jan 2017 01:57:40 +0000 (01:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=165282

Reviewed by Saam Barati.

JSTests:

* wasm/Builder.js: Add a Proxy to Builder.js, which intercepts
unknown property lookups. This creates way better error messages
on typos than 'undefined is not a function', which happens
semi-frequently as I typo opcode names (and which one is a typo is
hard to find because we chain builders).
(const._isValidValue):
(get target):
(const._importFunctionContinuation):
(const._importMemoryContinuation):
(const._importTableContinuation):
(const._exportFunctionContinuation):
(export.default.Builder.prototype._registerSectionBuilders.const.section.in.WASM.description.section.switch.section.case.string_appeared_here.this.section):
(export.default.Builder.prototype._registerSectionBuilders.this.Unknown):
* wasm/LowLevelBinary.js: Add limited support for var{u}int64 (only the 32-bit values)
(export.default.LowLevelBinary.prototype.varint32):
(export.default.LowLevelBinary.prototype.varuint64):
(export.default.LowLevelBinary.prototype.varint64):
* wasm/function-tests/exceptions.js: update error message
* wasm/function-tests/trap-load.js: update error message
* wasm/function-tests/trap-store.js: update error message
* wasm/js-api/wasm-to-wasm-bad-signature.js: Added. Test a bunch of bad wasm->wasm import signatures
(const.makeImportee.signature.switch):
(BadSignatureDropStartParams):
* wasm/js-api/wasm-to-wasm.js: Added. Test 64-bit wasm->wasm import calls
(const.callerModule):
(const.calleeModule):
(WasmToWasm):

Source/JavaScriptCore:

  - Add a new JSType for WebAssemblyFunction, and use it when creating its
    structure. This will is used to quickly detect from wasm whether the import
    call is to another wasm module, or whether it's to JS.
  - Generate two stubs from the import stub generator: one for wasm->JS and one
    for wasm -> wasm. This is done at Module time. Which is called will only be
    known at Instance time, once we've received the import object. We want to
    avoid codegen at Instance time, so having both around is great.
  - Restore the WebAssembly global state (VM top Instance, and pinned registers)
    after call / call_indirect, and in the JS->wasm entry stub.
  - Pinned registers are now a global thing, not per-Memory, because the wasm ->
    wasm stubs are generated at Module time where we don't really have enough
    information to do the right thing (doing so would generate too much code).

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* runtime/JSType.h: add WebAssemblyFunctionType as a JSType
* wasm/WasmB3IRGenerator.cpp: significantly rework how calls which
could be external work, and how we save / restore global state:
VM's top Instance, and pinned registers
(JSC::Wasm::B3IRGenerator::B3IRGenerator):
(JSC::Wasm::getMemoryBaseAndSize):
(JSC::Wasm::restoreWebAssemblyGlobalState):
(JSC::Wasm::createJSToWasmWrapper):
(JSC::Wasm::parseAndCompile):
* wasm/WasmB3IRGenerator.h:
* wasm/WasmBinding.cpp:
(JSC::Wasm::materializeImportJSCell):
(JSC::Wasm::wasmToJS):
(JSC::Wasm::wasmToWasm): the main goal of this patch was adding this function
(JSC::Wasm::exitStubGenerator):
* wasm/WasmBinding.h:
* wasm/WasmFormat.h: Get rid of much of the function index space:
we already have all of its information elsewhere, and as-is it
provides no extra efficiency.
(JSC::Wasm::ModuleInformation::functionIndexSpaceSize):
(JSC::Wasm::ModuleInformation::isImportedFunctionFromFunctionIndexSpace):
(JSC::Wasm::ModuleInformation::signatureIndexFromFunctionIndexSpace):
* wasm/WasmFunctionParser.h:
(JSC::Wasm::FunctionParser<Context>::FunctionParser):
* wasm/WasmMemory.cpp: Add some logging.
(JSC::Wasm::Memory::dump): this was nice when debugging
(JSC::Wasm::Memory::makeString):
(JSC::Wasm::Memory::Memory):
(JSC::Wasm::Memory::~Memory):
(JSC::Wasm::Memory::grow):
* wasm/WasmMemory.h: don't use extra indirection, it wasn't
needed. Reorder some of the fields which are looked up at runtime
so they're more cache-friendly.
(JSC::Wasm::Memory::Memory):
(JSC::Wasm::Memory::mode):
(JSC::Wasm::Memory::offsetOfSize):
* wasm/WasmMemoryInformation.cpp: Pinned registers are now a
global thing for all of JSC, not a per-Memory thing
anymore. wasm->wasm calls are more complex otherwise: they have to
figure out how to bridge between the caller and callee's
special-snowflake pinning.
(JSC::Wasm::PinnedRegisterInfo::get):
(JSC::Wasm::PinnedRegisterInfo::PinnedRegisterInfo):
(JSC::Wasm::MemoryInformation::MemoryInformation):
* wasm/WasmMemoryInformation.h:
* wasm/WasmModuleParser.cpp:
* wasm/WasmModuleParser.h:
* wasm/WasmPageCount.cpp: Copied from Source/JavaScriptCore/wasm/WasmBinding.h.
(JSC::Wasm::PageCount::dump): nice for debugging
* wasm/WasmPageCount.h:
* wasm/WasmPlan.cpp:
(JSC::Wasm::Plan::parseAndValidateModule):
(JSC::Wasm::Plan::run):
* wasm/WasmPlan.h:
(JSC::Wasm::Plan::takeWasmExitStubs):
* wasm/WasmSignature.cpp:
(JSC::Wasm::Signature::toString):
(JSC::Wasm::Signature::dump):
* wasm/WasmSignature.h:
* wasm/WasmValidate.cpp:
(JSC::Wasm::validateFunction):
* wasm/WasmValidate.h:
* wasm/js/JSWebAssemblyInstance.h:
(JSC::JSWebAssemblyInstance::offsetOfTable):
(JSC::JSWebAssemblyInstance::offsetOfImportFunctions):
(JSC::JSWebAssemblyInstance::offsetOfImportFunction):
* wasm/js/JSWebAssemblyMemory.cpp:
(JSC::JSWebAssemblyMemory::create):
(JSC::JSWebAssemblyMemory::JSWebAssemblyMemory):
(JSC::JSWebAssemblyMemory::buffer):
(JSC::JSWebAssemblyMemory::grow):
* wasm/js/JSWebAssemblyMemory.h:
(JSC::JSWebAssemblyMemory::memory):
(JSC::JSWebAssemblyMemory::offsetOfMemory):
(JSC::JSWebAssemblyMemory::offsetOfSize):
* wasm/js/JSWebAssemblyModule.cpp:
(JSC::JSWebAssemblyModule::create):
(JSC::JSWebAssemblyModule::JSWebAssemblyModule):
* wasm/js/JSWebAssemblyModule.h:
(JSC::JSWebAssemblyModule::signatureIndexFromFunctionIndexSpace):
(JSC::JSWebAssemblyModule::functionImportCount):
* wasm/js/WebAssemblyFunction.cpp:
(JSC::callWebAssemblyFunction):
(JSC::WebAssemblyFunction::create):
(JSC::WebAssemblyFunction::createStructure):
(JSC::WebAssemblyFunction::WebAssemblyFunction):
(JSC::WebAssemblyFunction::finishCreation):
* wasm/js/WebAssemblyFunction.h:
(JSC::WebAssemblyFunction::wasmEntrypoint):
(JSC::WebAssemblyFunction::offsetOfInstance):
(JSC::WebAssemblyFunction::offsetOfWasmEntryPointCode):
* wasm/js/WebAssemblyInstanceConstructor.cpp:
(JSC::constructJSWebAssemblyInstance): always start with a dummy
memory, so wasm->wasm calls don't need to null-check
* wasm/js/WebAssemblyMemoryConstructor.cpp:
(JSC::constructJSWebAssemblyMemory):
* wasm/js/WebAssemblyModuleConstructor.cpp:
(JSC::WebAssemblyModuleConstructor::createModule):
* wasm/js/WebAssemblyModuleRecord.cpp:
(JSC::WebAssemblyModuleRecord::link):
(JSC::WebAssemblyModuleRecord::evaluate):
* wasm/js/WebAssemblyModuleRecord.h:

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

44 files changed:
JSTests/ChangeLog
JSTests/wasm/Builder.js
JSTests/wasm/LowLevelBinary.js
JSTests/wasm/function-tests/exceptions.js
JSTests/wasm/function-tests/trap-load.js
JSTests/wasm/function-tests/trap-store.js
JSTests/wasm/js-api/wasm-to-wasm-bad-signature.js [new file with mode: 0644]
JSTests/wasm/js-api/wasm-to-wasm.js [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/JSType.h
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/WasmB3IRGenerator.h
Source/JavaScriptCore/wasm/WasmBinding.cpp
Source/JavaScriptCore/wasm/WasmBinding.h
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmFunctionParser.h
Source/JavaScriptCore/wasm/WasmMemory.cpp
Source/JavaScriptCore/wasm/WasmMemory.h
Source/JavaScriptCore/wasm/WasmMemoryInformation.cpp
Source/JavaScriptCore/wasm/WasmMemoryInformation.h
Source/JavaScriptCore/wasm/WasmModuleParser.cpp
Source/JavaScriptCore/wasm/WasmModuleParser.h
Source/JavaScriptCore/wasm/WasmPageCount.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmPageCount.h
Source/JavaScriptCore/wasm/WasmPlan.cpp
Source/JavaScriptCore/wasm/WasmPlan.h
Source/JavaScriptCore/wasm/WasmSignature.cpp
Source/JavaScriptCore/wasm/WasmSignature.h
Source/JavaScriptCore/wasm/WasmValidate.cpp
Source/JavaScriptCore/wasm/WasmValidate.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.h
Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyMemoryConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp

index 93cd6b8..3d48e6d 100644 (file)
@@ -1,3 +1,38 @@
+2017-01-02  JF Bastien  <jfbastien@apple.com>
+
+        WebAssembly: handle and optimize wasm export → wasm import calls
+        https://bugs.webkit.org/show_bug.cgi?id=165282
+
+        Reviewed by Saam Barati.
+
+        * wasm/Builder.js: Add a Proxy to Builder.js, which intercepts
+        unknown property lookups. This creates way better error messages
+        on typos than 'undefined is not a function', which happens
+        semi-frequently as I typo opcode names (and which one is a typo is
+        hard to find because we chain builders).
+        (const._isValidValue):
+        (get target):
+        (const._importFunctionContinuation):
+        (const._importMemoryContinuation):
+        (const._importTableContinuation):
+        (const._exportFunctionContinuation):
+        (export.default.Builder.prototype._registerSectionBuilders.const.section.in.WASM.description.section.switch.section.case.string_appeared_here.this.section):
+        (export.default.Builder.prototype._registerSectionBuilders.this.Unknown):
+        * wasm/LowLevelBinary.js: Add limited support for var{u}int64 (only the 32-bit values)
+        (export.default.LowLevelBinary.prototype.varint32):
+        (export.default.LowLevelBinary.prototype.varuint64):
+        (export.default.LowLevelBinary.prototype.varint64):
+        * wasm/function-tests/exceptions.js: update error message
+        * wasm/function-tests/trap-load.js: update error message
+        * wasm/function-tests/trap-store.js: update error message
+        * wasm/js-api/wasm-to-wasm-bad-signature.js: Added. Test a bunch of bad wasm->wasm import signatures
+        (const.makeImportee.signature.switch):
+        (BadSignatureDropStartParams):
+        * wasm/js-api/wasm-to-wasm.js: Added. Test 64-bit wasm->wasm import calls
+        (const.callerModule):
+        (const.calleeModule):
+        (WasmToWasm):
+
 2017-01-02  Saam Barati  <sbarati@apple.com>
 
         WebAssembly: Some loads don't take into account the offset
index da584c4..26ba66e 100644 (file)
@@ -38,7 +38,7 @@ const _isValidValue = (value, type) => {
     switch (type) {
     // We allow both signed and unsigned numbers.
     case "i32": return Math.round(value) === value && LLB.varint32Min <= value && value <= LLB.varuint32Max;
-    case "i64": throw new Error(`Unimplemented: value check for ${type}`); // FIXME https://bugs.webkit.org/show_bug.cgi?id=163420 64-bit values
+    case "i64": return true; // FIXME https://bugs.webkit.org/show_bug.cgi?id=163420 64-bit values
     case "f32": return typeof(value) === "number" && isFinite(value);
     case "f64": return typeof(value) === "number" && isFinite(value);
     default: throw new Error(`Implementation problem: unknown type ${type}`);
@@ -57,6 +57,16 @@ const _normalizeFunctionSignature = (params, ret) => {
     return [params, ret];
 };
 
+const _errorHandlingProxyFor = builder => builder["__isProxy"] ? builder : new Proxy(builder, {
+    get: (target, property, receiver) => {
+        if (property === "__isProxy")
+            return true;
+        if (target[property] === undefined)
+            throw new Error(`WebAssembly builder received unknown property '${property}'`);
+        return target[property];
+    }
+});
+
 const _maybeRegisterType = (builder, type) => {
     const typeSection = builder._getSection("Type");
     if (typeof(type) === "number") {
@@ -100,7 +110,7 @@ const _importFunctionContinuation = (builder, section, nextBuilder) => {
         section.data.push({ field: field, type: type, kind: "Function", module: module });
         // Imports also count in the function index space. Map them as objects to avoid clashing with Code functions' names.
         builder._registerFunctionToIndexSpace({ module: module, field: field });
-        return nextBuilder;
+        return _errorHandlingProxyFor(nextBuilder);
     };
 };
 
@@ -109,7 +119,7 @@ const _importMemoryContinuation = (builder, section, nextBuilder) => {
         assert.isString(module, `Import Memory module should be a string, got "${module}"`);
         assert.isString(field, `Import Memory field should be a string, got "${field}"`);
         section.data.push({module, field, kind: "Memory", memoryDescription: {initial, maximum}});
-        return nextBuilder;
+        return _errorHandlingProxyFor(nextBuilder);
     };
 };
 
@@ -118,7 +128,7 @@ const _importTableContinuation = (builder, section, nextBuilder) => {
         assert.isString(module, `Import Table module should be a string, got "${module}"`);
         assert.isString(field, `Import Table field should be a string, got "${field}"`);
         section.data.push({module, field, kind: "Table", tableDescription: {initial, maximum, element}});
-        return nextBuilder;
+        return _errorHandlingProxyFor(nextBuilder);
     };
 };
 
@@ -172,7 +182,7 @@ const _exportFunctionContinuation = (builder, section, nextBuilder) => {
                 assert.eq(type, exportedImport.type, `Re-exporting import "${exportedImport.field}" as "${field}" has mismatching type`);
         }
         section.data.push({ field: field, type: type, kind: "Function", index: index });
-        return nextBuilder;
+        return _errorHandlingProxyFor(nextBuilder);
     };
 };
 
@@ -189,7 +199,7 @@ const _exportGlobalContinuation = (builder, section, nextBuilder) => {
     return (field, index) => {
         assert.isNumber(index, `Global exports only support number indices right now`);
         section.data.push({ field, kind: "Global", index });
-        return nextBuilder;
+        return _errorHandlingProxyFor(nextBuilder);
     }
 };
 
@@ -197,7 +207,7 @@ const _exportMemoryContinuation = (builder, section, nextBuilder) => {
     return (field, index) => {
         assert.isNumber(index, `Memory exports only support number indices`);
         section.data.push({field, kind: "Memory", index});
-        return nextBuilder;
+        return _errorHandlingProxyFor(nextBuilder);
     }
 };
 
@@ -205,7 +215,7 @@ const _exportTableContinuation = (builder, section, nextBuilder) => {
     return (field, index) => {
         assert.isNumber(index, `Table exports only support number indices`);
         section.data.push({field, kind: "Table", index});
-        return nextBuilder;
+        return _errorHandlingProxyFor(nextBuilder);
     }
 };
 
@@ -220,10 +230,10 @@ const _importGlobalContinuation = (builder, section, nextBuilder) => {
                 assert.isString(field, `Import global field should be a string, got "${field}"`);
                 assert.isString(mutability, `Import global mutability should be a string, got "${mutability}"`);
                 section.data.push({ globalDescription: { type: op, mutability: _normalizeMutability(mutability) }, module, field, kind: "Global" });
-                return globalBuilder;
+                return _errorHandlingProxyFor(globalBuilder);
             };
         }
-        return globalBuilder;
+        return _errorHandlingProxyFor(globalBuilder);
     };
 };
 
@@ -324,11 +334,11 @@ const _createFunctionBuilder = (func, builder, previousBuilder) => {
             default:
                 nextBuilder = functionBuilder;
                 break;
-                case "End":
+            case "End":
                 nextBuilder = previousBuilder;
-                    break;
+                break;
             case "Block":
-                case "Loop":
+            case "Loop":
             case "If":
                 nextBuilder = _createFunctionBuilder(func, builder, functionBuilder);
                 break;
@@ -344,11 +354,12 @@ const _createFunctionBuilder = (func, builder, previousBuilder) => {
             const stackArgs = []; // FIXME https://bugs.webkit.org/show_bug.cgi?id=162706
             func.code.push({ name: op, value: value, arguments: stackArgs, immediates: imms });
             if (hasContinuation)
-                return continuation(nextBuilder).End();
-            return nextBuilder;
+                return _errorHandlingProxyFor(continuation(nextBuilder).End());
+            return _errorHandlingProxyFor(nextBuilder);
         };
-    }
-    return functionBuilder;
+    };
+
+    return _errorHandlingProxyFor(functionBuilder);
 }
 
 const _createFunction = (section, builder, previousBuilder) => {
@@ -449,10 +460,10 @@ export default class Builder {
                         Func: (params, ret) => {
                             [params, ret] = _normalizeFunctionSignature(params, ret);
                             s.data.push({ params: params, ret: ret });
-                            return typeBuilder;
+                            return _errorHandlingProxyFor(typeBuilder);
                         },
                     };
-                    return typeBuilder;
+                    return _errorHandlingProxyFor(typeBuilder);
                 };
                 break;
 
@@ -466,7 +477,7 @@ export default class Builder {
                     importBuilder.Function = _importFunctionContinuation(this, s, importBuilder);
                     importBuilder.Memory = _importMemoryContinuation(this, s, importBuilder);
                     importBuilder.Table = _importTableContinuation(this, s, importBuilder);
-                    return importBuilder;
+                    return _errorHandlingProxyFor(importBuilder);
                 };
                 break;
 
@@ -477,7 +488,7 @@ export default class Builder {
                         End: () => this
                         // FIXME: add ability to add this with whatever.
                     };
-                    return functionBuilder;
+                    return _errorHandlingProxyFor(functionBuilder);
                 };
                 break;
 
@@ -488,10 +499,10 @@ export default class Builder {
                         End: () => this,
                         Table: ({initial, maximum, element}) => {
                             s.data.push({tableDescription: {initial, maximum, element}});
-                            return tableBuilder;
+                            return _errorHandlingProxyFor(tableBuilder);
                         }
                     };
-                    return tableBuilder;
+                    return _errorHandlingProxyFor(tableBuilder);
                 };
                 break;
 
@@ -502,10 +513,10 @@ export default class Builder {
                         End: () => this,
                         InitialMaxPages: (initial, max) => {
                             s.data.push({ initial, max });
-                            return memoryBuilder;
+                            return _errorHandlingProxyFor(memoryBuilder);
                         }
                     };
-                    return memoryBuilder;
+                    return _errorHandlingProxyFor(memoryBuilder);
                 };
                 break;
 
@@ -516,16 +527,16 @@ export default class Builder {
                         End: () => this,
                         GetGlobal: (type, initValue, mutability) => {
                             s.data.push({ type, op: "get_global", mutability: _normalizeMutability(mutability), initValue });
-                            return globalBuilder;
+                            return _errorHandlingProxyFor(globalBuilder);
                         }
                     };
                     for (let op of WASM.description.value_type) {
                         globalBuilder[_toJavaScriptName(op)] = (initValue, mutability) => {
                             s.data.push({ type: op, op: op + ".const", mutability: _normalizeMutability(mutability), initValue });
-                            return globalBuilder;
+                            return _errorHandlingProxyFor(globalBuilder);
                         };
                     }
-                    return globalBuilder;
+                    return _errorHandlingProxyFor(globalBuilder);
                 };
                 break;
 
@@ -539,7 +550,7 @@ export default class Builder {
                     exportBuilder.Function = _exportFunctionContinuation(this, s, exportBuilder);
                     exportBuilder.Memory = _exportMemoryContinuation(this, s, exportBuilder);
                     exportBuilder.Table = _exportTableContinuation(this, s, exportBuilder);
-                    return exportBuilder;
+                    return _errorHandlingProxyFor(exportBuilder);
                 };
                 break;
 
@@ -552,7 +563,7 @@ export default class Builder {
                     if (typeof(functionIndexOrName) !== "number" && typeof(functionIndexOrName) !== "string")
                         throw new Error(`Start section's function index  must either be a number or a string`);
                     s.data.push(functionIndexOrName);
-                    return startBuilder;
+                    return _errorHandlingProxyFor(startBuilder);
                 };
                 break;
 
@@ -563,11 +574,11 @@ export default class Builder {
                         End: () => this,
                         Element: ({tableIndex = 0, offset, functionIndices}) => {
                             s.data.push({tableIndex, offset, functionIndices});
-                            return elementBuilder;
+                            return _errorHandlingProxyFor(elementBuilder);
                         }
                     };
 
-                    return elementBuilder;
+                    return _errorHandlingProxyFor(elementBuilder);
                 };
                 break;
 
@@ -632,12 +643,12 @@ export default class Builder {
                                     // FIXME in checked mode, test that the type is acceptable for start function. We probably want _registerFunctionToIndexSpace to also register types per index. https://bugs.webkit.org/show_bug.cgi?id=165658
                                 }
                             }
-                            return builder;
+                            return _errorHandlingProxyFor(builder);
                         },
 
                     };
                     codeBuilder.Function = _createFunction(s, builder, codeBuilder);
-                    return codeBuilder;
+                    return _errorHandlingProxyFor(codeBuilder);
                 };
                 break;
 
@@ -660,19 +671,19 @@ export default class Builder {
                                 Index: index => {
                                     assert.eq(index, 0); // Linear memory index must be zero in MVP.
                                     thisSegment.index = index;
-                                    return segmentBuilder;
+                                    return _errorHandlingProxyFor(segmentBuilder);
                                 },
                                 Offset: offset => {
                                     // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700
                                     assert.isNumber(offset);
                                     thisSegment.offset = offset;
-                                    return segmentBuilder;
+                                    return _errorHandlingProxyFor(segmentBuilder);
                                 },
                             };
-                            return segmentBuilder;
+                            return _errorHandlingProxyFor(segmentBuilder);
                         },
                     };
-                    return dataBuilder;
+                    return _errorHandlingProxyFor(dataBuilder);
                 };
                 break;
 
@@ -690,10 +701,10 @@ export default class Builder {
                 Byte: b => {
                     assert.eq(b & 0xFF, b, `Unknown section expected byte, got: "${b}"`);
                     s.data.push(b);
-                    return unknownBuilder;
+                    return _errorHandlingProxyFor(unknownBuilder);
                 }
             };
-            return unknownBuilder;
+            return _errorHandlingProxyFor(unknownBuilder);
         };
     }
     _addSection(nameOrNumber, extraObject) {
index 611a26d..f9bc8a3 100644 (file)
@@ -170,6 +170,18 @@ export default class LowLevelBinary {
             this.uint8(0x80 | b);
         } while (true);
     }
+    varuint64(v) {
+        assert.isNumber(v);
+        if (v < varuint32Min || varuint32Max < v)
+            throw new RangeError(`unimplemented: varuint64 larger than 32-bit`);
+        this.varuint32(v); // FIXME implement 64-bit var{u}int https://bugs.webkit.org/show_bug.cgi?id=163420
+    }
+    varint64(v) {
+        assert.isNumber(v);
+        if (v < varint32Min || varint32Max < v)
+            throw new RangeError(`unimplemented: varint64 larger than 32-bit`);
+        this.varint32(v); // FIXME implement 64-bit var{u}int https://bugs.webkit.org/show_bug.cgi?id=163420
+    }
     varuint1(v) {
         if (v !== 0 && v !== 1)
             throw new RangeError(`Invalid varuint1 ${v} range is [0, 1]`);
index d2b3a49..952513f 100644 (file)
@@ -45,18 +45,18 @@ function makeInstance() {
     assert.eq(table.get(0), null);
 
     for (let i = 0; i < 1000; i++) {
-        assert.throws(() => foo(0, i), WebAssembly.RuntimeError, "call_indirect to a null table entry (evaluating 'func(...args)')");
+        assert.throws(() => foo(0, i), WebAssembly.RuntimeError, "call_indirect to a null table entry");
     }
 
     table.set(0, foo);
     assert.eq(table.get(0), foo);
 
     for (let i = 0; i < 1000; i++) {
-        assert.throws(() => foo(1 + i, i), WebAssembly.RuntimeError, "Out of bounds call_indirect (evaluating 'func(...args)')");
+        assert.throws(() => foo(1 + i, i), WebAssembly.RuntimeError, "Out of bounds call_indirect");
     }
 
     for (let i = 0; i < 1000; i++) {
-        assert.throws(() => foo(0, i), WebAssembly.RuntimeError, "call_indirect to a signature that does not match (evaluating 'func(...args)')");
+        assert.throws(() => foo(0, i), WebAssembly.RuntimeError, "call_indirect to a signature that does not match");
     }
 
     table.set(0, bar);
index 4dd82dc..1231e03 100644 (file)
@@ -29,6 +29,6 @@ function wasmFrameCountFromError(e) {
 }
 
 for (let i = 0; i < 1000; i++) {
-    const e = assert.throws(() => foo(numPages * pageSize + 1), WebAssembly.RuntimeError, "Out of bounds memory access (evaluating 'func(...args)')");
+    const e = assert.throws(() => foo(numPages * pageSize + 1), WebAssembly.RuntimeError, "Out of bounds memory access");
     assert.eq(wasmFrameCountFromError(e), 2);
 }
index 55987c2..365713a 100644 (file)
@@ -25,5 +25,5 @@ const numPages = 10;
     const foo = new WebAssembly.Instance(module, {a: {b: new WebAssembly.Memory({initial: numPages})}}).exports.foo;
 
     for (let i = 0; i < 10000; i++)
-        assert.throws(() => foo(i, numPages * pageSize + 1), WebAssembly.RuntimeError, "Out of bounds memory access (evaluating 'func(...args)')");
+        assert.throws(() => foo(i, numPages * pageSize + 1), WebAssembly.RuntimeError, "Out of bounds memory access");
 }
diff --git a/JSTests/wasm/js-api/wasm-to-wasm-bad-signature.js b/JSTests/wasm/js-api/wasm-to-wasm-bad-signature.js
new file mode 100644 (file)
index 0000000..d69ba2c
--- /dev/null
@@ -0,0 +1,105 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const importName = "pierOne";
+const types = ["i32", "i64", "f32", "f64", "void"];
+const typesNonVoid = ["i32", "i64", "f32", "f64"];
+const swapType = (type, index) => types[(types.indexOf(type) + index) % types.length];
+const swapTypeNonVoid = (type, index) => typesNonVoid[(typesNonVoid.indexOf(type) + index) % typesNonVoid.length];
+
+const signatures = [
+    { params: ["i32"], ret: "void" },
+    { params: ["i64"], ret: "void" },
+    { params: ["f32"], ret: "void" },
+    { params: ["f64"], ret: "void" },
+    { params: ["i32"], ret: "i32" },
+    { params: ["i64"], ret: "i64" },
+    { params: ["f32"], ret: "f32" },
+    { params: ["f64"], ret: "f64" },
+    { params: ["i32", "f32"], ret: "i32" },
+    { params: ["f32", "i32"], ret: "i32" },
+    { params: ["i64", "f64"], ret: "i64" },
+    { params: ["f64", "i64"], ret: "i64" },
+    { params: ["i32", "f32", "i32"], ret: "i32" },
+    { params: ["i32", "f32", "i32"], ret: "i32" },
+    { params: ["i64", "f64", "i64"], ret: "i64" },
+    { params: ["i64", "f64", "i64"], ret: "i64" },
+    { params: Array(32).fill("i32"), ret: "i64" },
+    { params: Array(32).fill("i64"), ret: "i64" },
+    { params: Array(32).fill("f32"), ret: "i64" },
+    { params: Array(32).fill("f64"), ret: "i64" },
+];
+
+const makeImporter = signature => {
+    const builder = (new Builder())
+        .Type().End()
+        .Import().Function("exports", importName, signature).End();
+    return new WebAssembly.Module(builder.WebAssembly().get());
+};
+
+const makeImportee = signature => {
+    const builder = (new Builder())
+        .Type().End()
+        .Function().End()
+        .Export()
+            .Function(importName)
+        .End()
+        .Code()
+            .Function(importName, signature);
+    switch (signature.ret) {
+    case "i32": builder.I32Const(0); break;
+    case "i64": builder.I64Const(0); break;
+    case "f32": builder.F32Const(0); break;
+    case "f64": builder.F64Const(0); break;
+    case "void": break;
+    }
+    return new WebAssembly.Instance(new WebAssembly.Module(builder.Return().End().End().WebAssembly().get()));
+};
+
+(function BadSignatureDropStartParams() {
+    for (let signature of signatures) {
+        const importee = makeImportee(signature);
+        for (let i = 1; i <= signature.params.length; ++i) {
+            const badParamSignature = { params: signature.params.slice(i, signature.params.length), ret: signature.ret };
+            const importer = makeImporter(badParamSignature);
+            assert.throws(() => new WebAssembly.Instance(importer, importee), WebAssembly.LinkError, `imported function's signature doesn't match the provided WebAssembly function's signature (evaluating 'new WebAssembly.Instance(importer, importee)')`);
+        }
+    }
+})();
+
+(function BadSignatureDropEndParams() {
+    for (let signature of signatures) {
+        const importee = makeImportee(signature);
+        for (let i = 1; i < signature.params.length; ++i) {
+            const badParamSignature = { params: signature.params.slice(0, i), ret: signature.ret };
+            const importer = makeImporter(badParamSignature);
+            assert.throws(() => new WebAssembly.Instance(importer, importee), WebAssembly.LinkError, `imported function's signature doesn't match the provided WebAssembly function's signature (evaluating 'new WebAssembly.Instance(importer, importee)')`);
+        }
+    }
+})();
+
+(function BadSignatureSwapParam() {
+    for (let signature of signatures) {
+        const importee = makeImportee(signature);
+        for (let signatureIndex = 0; signatureIndex < signature.length; ++signatureIndex) {
+            for (let typeIndex = 1; typeIndex < typesNonVoid.length; ++typeIndex) {
+                let badParams = signature.params.slice();
+                badParams[signatureIndex] = swapTypeNonVoid(badParams[signatureIndex], typeIndex);
+                const badParamSignature = { params: badParams, ret: signature.ret };
+                const importer = makeImporter(badParamSignature);
+                assert.throws(() => new WebAssembly.Instance(importer, importee), WebAssembly.LinkError, `imported function's signature doesn't match the provided WebAssembly function's signature (evaluating 'new WebAssembly.Instance(importer, importee)')`);
+            }
+        }
+    }
+})();
+
+(function BadSignatureRet() {
+    for (let signature of signatures) {
+        const importee = makeImportee(signature);
+        for (let typeIndex = 1; typeIndex < types.length; ++typeIndex) {
+            const badParamSignature = { params: signature.params, ret: swapType(signature.ret, typeIndex) };
+            const importer = makeImporter(badParamSignature);
+            assert.throws(() => new WebAssembly.Instance(importer, importee), WebAssembly.LinkError, `imported function's signature doesn't match the provided WebAssembly function's signature (evaluating 'new WebAssembly.Instance(importer, importee)')`);
+        }
+    }
+})();
diff --git a/JSTests/wasm/js-api/wasm-to-wasm.js b/JSTests/wasm/js-api/wasm-to-wasm.js
new file mode 100644 (file)
index 0000000..2df46e6
--- /dev/null
@@ -0,0 +1,72 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const callerTopBits = 0xC0FEBEEF;
+const innerReturnHi = 0xDEADFACE;
+const innerReturnLo = 0xC0FEC0FE;
+
+const callerModule = () => {
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Function("exports", "callMe", { params: ["i64"], ret: "i64" })
+        .End()
+        .Function().End()
+        .Export()
+            .Function("entry")
+        .End()
+        .Code()
+            .Function("entry", { params: ["i32"], ret: "i32" }, ["i64"])
+                .I32Const(callerTopBits).I64ExtendUI32().I32Const(32).I64ExtendUI32().I64Shl() // ((i64)callerTopBits) << 32
+                .GetLocal(0).I64ExtendUI32()
+                .I64Or() // value: param | (((i64)callerTopBits << 32))
+                .Call(0) // Calls exports.callMe(param | (((i64)callerTopBits) << 32)).
+                .TeeLocal(1).I32WrapI64() // lo: (i32)callResult
+                .GetLocal(1).I32Const(32).I64ExtendUI32().I64ShrU().I32WrapI64() // hi: (i32)(callResult >> 32)
+                .I32Xor()
+                .Return()
+            .End()
+        .End();
+    return new WebAssembly.Module(builder.WebAssembly().get());
+};
+
+const calleeModule = () => {
+    const builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Function("imp", "func", { params: ["i32", "i32"], ret: "i32" })
+        .End()
+        .Function().End()
+        .Export()
+            .Function("callMe")
+        .End()
+        .Code()
+            .Function("callMe", { params: ["i64"], ret: "i64" })
+                .GetLocal(0).I32WrapI64() // lo: (i32)param
+                .GetLocal(0).I32Const(32).I64ExtendUI32().I64ShrU().I32WrapI64() // hi: (i32)(param >> 32)
+                .Call(0) // Calls imp.func with the 64-bit value as i32 { hi, lo }.
+                .Drop()
+                .I32Const(innerReturnHi).I64ExtendUI32().I32Const(32).I64ExtendUI32().I64Shl().I32Const(innerReturnLo).I64ExtendUI32().I64Or() // ((i64)hi << 32) | (i64)lo
+                .Return()
+            .End()
+        .End();
+    return new WebAssembly.Module(builder.WebAssembly().get());
+};
+
+(function WasmToWasm() {
+    let value;
+    const func = (hi, lo) => { value = { hi: hi, lo: lo }; return hi ^ lo; };
+    const callee = new WebAssembly.Instance(calleeModule(), { imp: { func: func } });
+    const caller = new WebAssembly.Instance(callerModule(), callee);
+    for (let i = 0; i < 4096; ++i) {
+        assert.eq(caller.exports.entry(i), innerReturnHi ^ innerReturnLo);
+        assert.eq(value.lo >>> 0, callerTopBits);
+        assert.eq(value.hi >>> 0, i);
+    }
+})();
+
+// FIXME test the following https://bugs.webkit.org/show_bug.cgi?id=166625
+// - wasm->wasm using 32-bit things (including float), as well as 64-bit NaNs that don't get canonicalized
+// - Do a throw two-deep
+// - Check that the first wasm's instance is back in OK state (with table or global?)
+// - Test calling through a Table
index 969a123..9de1a3a 100644 (file)
@@ -905,6 +905,7 @@ set(JavaScriptCore_SOURCES
     wasm/WasmMemory.cpp
     wasm/WasmMemoryInformation.cpp
     wasm/WasmModuleParser.cpp
+    wasm/WasmPageCount.cpp
     wasm/WasmPlan.cpp
     wasm/WasmSignature.cpp
     wasm/WasmValidate.cpp
index 8253d65..f85de72 100644 (file)
@@ -1,3 +1,128 @@
+2017-01-02  JF Bastien  <jfbastien@apple.com>
+
+        WebAssembly: handle and optimize wasm export → wasm import calls
+        https://bugs.webkit.org/show_bug.cgi?id=165282
+
+        Reviewed by Saam Barati.
+
+          - Add a new JSType for WebAssemblyFunction, and use it when creating its
+            structure. This will is used to quickly detect from wasm whether the import
+            call is to another wasm module, or whether it's to JS.
+          - Generate two stubs from the import stub generator: one for wasm->JS and one
+            for wasm -> wasm. This is done at Module time. Which is called will only be
+            known at Instance time, once we've received the import object. We want to
+            avoid codegen at Instance time, so having both around is great.
+          - Restore the WebAssembly global state (VM top Instance, and pinned registers)
+            after call / call_indirect, and in the JS->wasm entry stub.
+          - Pinned registers are now a global thing, not per-Memory, because the wasm ->
+            wasm stubs are generated at Module time where we don't really have enough
+            information to do the right thing (doing so would generate too much code).
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * runtime/JSType.h: add WebAssemblyFunctionType as a JSType
+        * wasm/WasmB3IRGenerator.cpp: significantly rework how calls which
+        could be external work, and how we save / restore global state:
+        VM's top Instance, and pinned registers
+        (JSC::Wasm::B3IRGenerator::B3IRGenerator):
+        (JSC::Wasm::getMemoryBaseAndSize):
+        (JSC::Wasm::restoreWebAssemblyGlobalState):
+        (JSC::Wasm::createJSToWasmWrapper):
+        (JSC::Wasm::parseAndCompile):
+        * wasm/WasmB3IRGenerator.h:
+        * wasm/WasmBinding.cpp:
+        (JSC::Wasm::materializeImportJSCell):
+        (JSC::Wasm::wasmToJS):
+        (JSC::Wasm::wasmToWasm): the main goal of this patch was adding this function
+        (JSC::Wasm::exitStubGenerator):
+        * wasm/WasmBinding.h:
+        * wasm/WasmFormat.h: Get rid of much of the function index space:
+        we already have all of its information elsewhere, and as-is it
+        provides no extra efficiency.
+        (JSC::Wasm::ModuleInformation::functionIndexSpaceSize):
+        (JSC::Wasm::ModuleInformation::isImportedFunctionFromFunctionIndexSpace):
+        (JSC::Wasm::ModuleInformation::signatureIndexFromFunctionIndexSpace):
+        * wasm/WasmFunctionParser.h:
+        (JSC::Wasm::FunctionParser<Context>::FunctionParser):
+        * wasm/WasmMemory.cpp: Add some logging.
+        (JSC::Wasm::Memory::dump): this was nice when debugging
+        (JSC::Wasm::Memory::makeString):
+        (JSC::Wasm::Memory::Memory):
+        (JSC::Wasm::Memory::~Memory):
+        (JSC::Wasm::Memory::grow):
+        * wasm/WasmMemory.h: don't use extra indirection, it wasn't
+        needed. Reorder some of the fields which are looked up at runtime
+        so they're more cache-friendly.
+        (JSC::Wasm::Memory::Memory):
+        (JSC::Wasm::Memory::mode):
+        (JSC::Wasm::Memory::offsetOfSize):
+        * wasm/WasmMemoryInformation.cpp: Pinned registers are now a
+        global thing for all of JSC, not a per-Memory thing
+        anymore. wasm->wasm calls are more complex otherwise: they have to
+        figure out how to bridge between the caller and callee's
+        special-snowflake pinning.
+        (JSC::Wasm::PinnedRegisterInfo::get):
+        (JSC::Wasm::PinnedRegisterInfo::PinnedRegisterInfo):
+        (JSC::Wasm::MemoryInformation::MemoryInformation):
+        * wasm/WasmMemoryInformation.h:
+        * wasm/WasmModuleParser.cpp:
+        * wasm/WasmModuleParser.h:
+        * wasm/WasmPageCount.cpp: Copied from Source/JavaScriptCore/wasm/WasmBinding.h.
+        (JSC::Wasm::PageCount::dump): nice for debugging
+        * wasm/WasmPageCount.h:
+        * wasm/WasmPlan.cpp:
+        (JSC::Wasm::Plan::parseAndValidateModule):
+        (JSC::Wasm::Plan::run):
+        * wasm/WasmPlan.h:
+        (JSC::Wasm::Plan::takeWasmExitStubs):
+        * wasm/WasmSignature.cpp:
+        (JSC::Wasm::Signature::toString):
+        (JSC::Wasm::Signature::dump):
+        * wasm/WasmSignature.h:
+        * wasm/WasmValidate.cpp:
+        (JSC::Wasm::validateFunction):
+        * wasm/WasmValidate.h:
+        * wasm/js/JSWebAssemblyInstance.h:
+        (JSC::JSWebAssemblyInstance::offsetOfTable):
+        (JSC::JSWebAssemblyInstance::offsetOfImportFunctions):
+        (JSC::JSWebAssemblyInstance::offsetOfImportFunction):
+        * wasm/js/JSWebAssemblyMemory.cpp:
+        (JSC::JSWebAssemblyMemory::create):
+        (JSC::JSWebAssemblyMemory::JSWebAssemblyMemory):
+        (JSC::JSWebAssemblyMemory::buffer):
+        (JSC::JSWebAssemblyMemory::grow):
+        * wasm/js/JSWebAssemblyMemory.h:
+        (JSC::JSWebAssemblyMemory::memory):
+        (JSC::JSWebAssemblyMemory::offsetOfMemory):
+        (JSC::JSWebAssemblyMemory::offsetOfSize):
+        * wasm/js/JSWebAssemblyModule.cpp:
+        (JSC::JSWebAssemblyModule::create):
+        (JSC::JSWebAssemblyModule::JSWebAssemblyModule):
+        * wasm/js/JSWebAssemblyModule.h:
+        (JSC::JSWebAssemblyModule::signatureIndexFromFunctionIndexSpace):
+        (JSC::JSWebAssemblyModule::functionImportCount):
+        * wasm/js/WebAssemblyFunction.cpp:
+        (JSC::callWebAssemblyFunction):
+        (JSC::WebAssemblyFunction::create):
+        (JSC::WebAssemblyFunction::createStructure):
+        (JSC::WebAssemblyFunction::WebAssemblyFunction):
+        (JSC::WebAssemblyFunction::finishCreation):
+        * wasm/js/WebAssemblyFunction.h:
+        (JSC::WebAssemblyFunction::wasmEntrypoint):
+        (JSC::WebAssemblyFunction::offsetOfInstance):
+        (JSC::WebAssemblyFunction::offsetOfWasmEntryPointCode):
+        * wasm/js/WebAssemblyInstanceConstructor.cpp:
+        (JSC::constructJSWebAssemblyInstance): always start with a dummy
+        memory, so wasm->wasm calls don't need to null-check
+        * wasm/js/WebAssemblyMemoryConstructor.cpp:
+        (JSC::constructJSWebAssemblyMemory):
+        * wasm/js/WebAssemblyModuleConstructor.cpp:
+        (JSC::WebAssemblyModuleConstructor::createModule):
+        * wasm/js/WebAssemblyModuleRecord.cpp:
+        (JSC::WebAssemblyModuleRecord::link):
+        (JSC::WebAssemblyModuleRecord::evaluate):
+        * wasm/js/WebAssemblyModuleRecord.h:
+
 2017-01-02  Saam Barati  <sbarati@apple.com>
 
         WebAssembly: Some loads don't take into account the offset
index d93256b..116369e 100644 (file)
                2ADFA26318EF3540004F9FCC /* GCLogging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2ADFA26218EF3540004F9FCC /* GCLogging.cpp */; };
                2AF7382C18BBBF92008A5A37 /* StructureIDTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2AF7382A18BBBF92008A5A37 /* StructureIDTable.cpp */; };
                2AF7382D18BBBF92008A5A37 /* StructureIDTable.h in Headers */ = {isa = PBXBuildFile; fileRef = 2AF7382B18BBBF92008A5A37 /* StructureIDTable.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               2D342F36F7244096804ADB24 /* SourceOrigin.h in Headers */ = {isa = PBXBuildFile; fileRef = 425BA1337E4344E1B269A671 /* SourceOrigin.h */; settings = {ATTRIBUTES = (Private, ); }; };
                371D842D17C98B6E00ECF994 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 371D842C17C98B6E00ECF994 /* libz.dylib */; };
                412952771D2CF6BC00E78B89 /* builtins_generate_internals_wrapper_header.py in Headers */ = {isa = PBXBuildFile; fileRef = 412952731D2CF6AC00E78B89 /* builtins_generate_internals_wrapper_header.py */; settings = {ATTRIBUTES = (Private, ); }; };
                412952781D2CF6BC00E78B89 /* builtins_generate_internals_wrapper_implementation.py in Headers */ = {isa = PBXBuildFile; fileRef = 412952741D2CF6AC00E78B89 /* builtins_generate_internals_wrapper_implementation.py */; settings = {ATTRIBUTES = (Private, ); }; };
                AD7438C01E0457A400FD0C2A /* WasmSignature.h in Headers */ = {isa = PBXBuildFile; fileRef = AD7438BF1E04579200FD0C2A /* WasmSignature.h */; settings = {ATTRIBUTES = (Private, ); }; };
                AD7438C11E0457AA00FD0C2A /* WasmSignature.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD7438BE1E04579200FD0C2A /* WasmSignature.cpp */; };
                AD86A93E1AA4D88D002FE77F /* WeakGCMapInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = AD86A93D1AA4D87C002FE77F /* WeakGCMapInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               ADB6F67D1E15D7600082F384 /* WasmPageCount.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ADB6F67C1E15D7500082F384 /* WasmPageCount.cpp */; };
                ADBC54D41DF8EA2B005BF738 /* WebAssemblyToJSCallee.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ADBC54D21DF8EA00005BF738 /* WebAssemblyToJSCallee.cpp */; };
                ADBC54D51DF8EA2B005BF738 /* WebAssemblyToJSCallee.h in Headers */ = {isa = PBXBuildFile; fileRef = ADBC54D31DF8EA00005BF738 /* WebAssemblyToJSCallee.h */; };
                ADDB1F6318D77DBE009B58A8 /* OpaqueRootSet.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDB1F6218D77DB7009B58A8 /* OpaqueRootSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
                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, ); }; };
-               2D342F36F7244096804ADB24 /* SourceOrigin.h in Headers */ = {isa = PBXBuildFile; fileRef = 425BA1337E4344E1B269A671 /* SourceOrigin.h */; settings = {ATTRIBUTES = (Private, ); }; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
                412952751D2CF6AC00E78B89 /* builtins_generate_wrapper_header.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = builtins_generate_wrapper_header.py; sourceTree = "<group>"; };
                412952761D2CF6AC00E78B89 /* builtins_generate_wrapper_implementation.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = builtins_generate_wrapper_implementation.py; sourceTree = "<group>"; };
                41DEA1311B9F3154006D65DD /* BuiltinUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BuiltinUtils.h; sourceTree = "<group>"; };
+               425BA1337E4344E1B269A671 /* SourceOrigin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SourceOrigin.h; sourceTree = "<group>"; };
                4319DA011C1BE3C1001D260B /* B3LowerMacrosAfterOptimizations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3LowerMacrosAfterOptimizations.cpp; path = b3/B3LowerMacrosAfterOptimizations.cpp; sourceTree = "<group>"; };
                4319DA021C1BE3C1001D260B /* B3LowerMacrosAfterOptimizations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3LowerMacrosAfterOptimizations.h; path = b3/B3LowerMacrosAfterOptimizations.h; sourceTree = "<group>"; };
                4340A4821A9051AF00D73CCA /* MathCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MathCommon.cpp; sourceTree = "<group>"; };
                AD7438BE1E04579200FD0C2A /* WasmSignature.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmSignature.cpp; sourceTree = "<group>"; };
                AD7438BF1E04579200FD0C2A /* WasmSignature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmSignature.h; sourceTree = "<group>"; };
                AD86A93D1AA4D87C002FE77F /* WeakGCMapInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WeakGCMapInlines.h; sourceTree = "<group>"; };
+               ADB6F67C1E15D7500082F384 /* WasmPageCount.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmPageCount.cpp; sourceTree = "<group>"; };
                ADBC54D21DF8EA00005BF738 /* WebAssemblyToJSCallee.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebAssemblyToJSCallee.cpp; path = js/WebAssemblyToJSCallee.cpp; sourceTree = "<group>"; };
                ADBC54D31DF8EA00005BF738 /* WebAssemblyToJSCallee.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebAssemblyToJSCallee.h; path = js/WebAssemblyToJSCallee.h; sourceTree = "<group>"; };
                ADDB1F6218D77DB7009B58A8 /* OpaqueRootSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpaqueRootSet.h; sourceTree = "<group>"; };
                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>"; };
-               425BA1337E4344E1B269A671 /* SourceOrigin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SourceOrigin.h; path = SourceOrigin.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
                                79B759721DFA4C600052174C /* WasmMemoryInformation.h */,
                                53F40E961D5A7BEC0099A1B6 /* WasmModuleParser.cpp */,
                                53F40E941D5A7AEF0099A1B6 /* WasmModuleParser.h */,
+                               ADB6F67C1E15D7500082F384 /* WasmPageCount.cpp */,
                                79B759731DFA4C600052174C /* WasmPageCount.h */,
                                53F40E8C1D5901F20099A1B6 /* WasmParser.h */,
                                531374BE1D5CE95000AF7A0B /* WasmPlan.cpp */,
                                0F64B2711A784BAF006E4E66 /* BinarySwitch.cpp in Sources */,
                                14280863107EC11A0013E7B2 /* BooleanConstructor.cpp in Sources */,
                                14280864107EC11A0013E7B2 /* BooleanObject.cpp in Sources */,
+                               ADB6F67D1E15D7600082F384 /* WasmPageCount.cpp in Sources */,
                                14280865107EC11A0013E7B2 /* BooleanPrototype.cpp in Sources */,
                                DE26E9071CB5DEFB00D2BE82 /* BuiltinExecutableCreator.cpp in Sources */,
                                A7D801A41880D66E0026C39B /* BuiltinExecutables.cpp in Sources */,
index 3eaf4a0..3a7a3f4 100644 (file)
@@ -2647,7 +2647,6 @@ static EncodedJSValue JSC_HOST_CALL functionTestWasmModuleFunctions(ExecState* e
                 lastIndex = calleeIndex;
             });
     }
-
     std::unique_ptr<Wasm::ModuleInformation> moduleInformation = plan.takeModuleInformation();
     RELEASE_ASSERT(!moduleInformation->memory);
 
index 05202f3..ac509d7 100644 (file)
@@ -93,6 +93,8 @@ enum JSType : uint8_t {
     JSMapType,
     JSSetType,
 
+    WebAssemblyFunctionType,
+
     LastJSCObjectType = JSSetType,
     MaxJSType = 0b11111111,
 };
index 3c2562b..80d7003 100644 (file)
@@ -155,7 +155,7 @@ public:
             return fail(__VA_ARGS__);             \
     } while (0)
 
-    B3IRGenerator(VM&, const ModuleInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&, const ImmutableFunctionIndexSpace&);
+    B3IRGenerator(VM&, const ModuleInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&);
 
     PartialResult WARN_UNUSED_RETURN addArguments(const Signature*);
     PartialResult WARN_UNUSED_RETURN addLocal(Type, uint32_t);
@@ -205,8 +205,6 @@ public:
 
     void emitExceptionCheck(CCallHelpers&, ExceptionType);
 
-    void emitReloadPinnedRegisters();
-
 private:
     ExpressionType emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOp);
     ExpressionType emitLoadOp(LoadOpType, Origin, ExpressionType pointer, uint32_t offset);
@@ -219,7 +217,6 @@ private:
     void emitChecksForModOrDiv(B3::Opcode, ExpressionType left, ExpressionType right);
 
     VM& m_vm;
-    const ImmutableFunctionIndexSpace& m_functionIndexSpace;
     const ModuleInformation& m_info;
     Procedure& m_proc;
     BasicBlock* m_currentBlock;
@@ -231,9 +228,8 @@ private:
     Value* m_instanceValue;
 };
 
-B3IRGenerator::B3IRGenerator(VM& vm, const ModuleInformation& info, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ImmutableFunctionIndexSpace& functionIndexSpace)
+B3IRGenerator::B3IRGenerator(VM& vm, const ModuleInformation& info, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls)
     : m_vm(vm)
-    , m_functionIndexSpace(functionIndexSpace)
     , m_info(info)
     , m_proc(procedure)
     , m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls)
@@ -254,14 +250,16 @@ B3IRGenerator::B3IRGenerator(VM& vm, const ModuleInformation& info, Procedure& p
         }
     }
 
-    if (info.hasMemory()) {
-        m_memoryBaseGPR = info.memory.pinnedRegisters().baseMemoryPointer;
-        m_proc.pinRegister(m_memoryBaseGPR);
-        ASSERT(!info.memory.pinnedRegisters().sizeRegisters[0].sizeOffset);
-        m_memorySizeGPR = info.memory.pinnedRegisters().sizeRegisters[0].sizeRegister;
-        for (const PinnedSizeRegisterInfo& regInfo : info.memory.pinnedRegisters().sizeRegisters)
-            m_proc.pinRegister(regInfo.sizeRegister);
+    // FIXME we don't really need to pin registers here if there's no memory. It makes wasm -> wasm thunks simpler for now. https://bugs.webkit.org/show_bug.cgi?id=166623
+    const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
+    m_memoryBaseGPR = pinnedRegs.baseMemoryPointer;
+    m_proc.pinRegister(m_memoryBaseGPR);
+    ASSERT(!pinnedRegs.sizeRegisters[0].sizeOffset);
+    m_memorySizeGPR = pinnedRegs.sizeRegisters[0].sizeRegister;
+    for (const PinnedSizeRegisterInfo& regInfo : pinnedRegs.sizeRegisters)
+        m_proc.pinRegister(regInfo.sizeRegister);
 
+    if (info.hasMemory()) {
         m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR, unsigned) {
             AllowMacroScratchRegisterUsage allowScratch(jit);
             ASSERT_UNUSED(pinnedGPR, m_memorySizeGPR == pinnedGPR);
@@ -275,6 +273,56 @@ B3IRGenerator::B3IRGenerator(VM& vm, const ModuleInformation& info, Procedure& p
         m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), &m_vm.topJSWebAssemblyInstance));
 }
 
+struct MemoryBaseAndSize {
+    Value* base;
+    Value* size;
+};
+
+static MemoryBaseAndSize getMemoryBaseAndSize(VM& vm, Value* instance, Procedure& proc, BasicBlock* block)
+{
+    Value* memoryObject = block->appendNew<MemoryValue>(proc, Load, pointerType(), Origin(), instance, JSWebAssemblyInstance::offsetOfMemory());
+
+    static_assert(sizeof(decltype(vm.topJSWebAssemblyInstance->memory()->memory()->memory())) == sizeof(void*), "codegen relies on this size");
+    static_assert(sizeof(decltype(vm.topJSWebAssemblyInstance->memory()->memory()->size())) == sizeof(uint64_t), "codegen relies on this size");
+    MemoryBaseAndSize result;
+    result.base = block->appendNew<MemoryValue>(proc, Load, pointerType(), Origin(), memoryObject, JSWebAssemblyMemory::offsetOfMemory());
+    result.size = block->appendNew<MemoryValue>(proc, Load, Int64, Origin(), memoryObject, JSWebAssemblyMemory::offsetOfSize());
+
+    return result;
+}
+
+static void restoreWebAssemblyGlobalState(VM& vm, const MemoryInformation& memory, Value* instance, Procedure& proc, BasicBlock* block)
+{
+    block->appendNew<MemoryValue>(proc, Store, Origin(), instance, block->appendNew<ConstPtrValue>(proc, Origin(), &vm.topJSWebAssemblyInstance));
+
+    if (!!memory) {
+        const PinnedRegisterInfo* pinnedRegs = &PinnedRegisterInfo::get();
+        RegisterSet clobbers;
+        clobbers.set(pinnedRegs->baseMemoryPointer);
+        for (auto info : pinnedRegs->sizeRegisters)
+            clobbers.set(info.sizeRegister);
+
+        B3::PatchpointValue* patchpoint = block->appendNew<B3::PatchpointValue>(proc, B3::Void, Origin());
+        patchpoint->effects = Effects::none();
+        patchpoint->effects.writesPinned = true;
+        patchpoint->clobber(clobbers);
+
+        patchpoint->append(instance, ValueRep::SomeRegister);
+
+        patchpoint->setGenerator([pinnedRegs] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
+            GPRReg baseMemory = pinnedRegs->baseMemoryPointer;
+            jit.loadPtr(CCallHelpers::Address(params[0].gpr(), JSWebAssemblyInstance::offsetOfMemory()), baseMemory);
+            const auto& sizeRegs = pinnedRegs->sizeRegisters;
+            ASSERT(sizeRegs.size() >= 1);
+            ASSERT(!sizeRegs[0].sizeOffset); // The following code assumes we start at 0, and calculates subsequent size registers relative to 0.
+            jit.loadPtr(CCallHelpers::Address(baseMemory, JSWebAssemblyMemory::offsetOfSize()), sizeRegs[0].sizeRegister);
+            jit.loadPtr(CCallHelpers::Address(baseMemory, JSWebAssemblyMemory::offsetOfMemory()), baseMemory);
+            for (unsigned i = 1; i < sizeRegs.size(); ++i)
+                jit.add64(CCallHelpers::TrustedImm32(-sizeRegs[i].sizeOffset), sizeRegs[0].sizeRegister, sizeRegs[i].sizeRegister);
+        });
+    }
+}
+
 void B3IRGenerator::emitExceptionCheck(CCallHelpers& jit, ExceptionType type)
 {
     jit.move(CCallHelpers::TrustedImm32(static_cast<uint32_t>(type)), GPRInfo::argumentGPR1);
@@ -338,49 +386,6 @@ auto B3IRGenerator::addUnreachable() -> PartialResult
     return { };
 }
 
-static void reloadPinnedRegisters(Procedure& proc, BasicBlock* block, const ModuleInformation& info, Value* instance)
-{
-    if (!info.hasMemory())
-        return;
-
-    const MemoryInformation* memory = &info.memory;
-
-    RegisterSet clobbers;
-    clobbers.set(memory->pinnedRegisters().baseMemoryPointer);
-    for (auto info : memory->pinnedRegisters().sizeRegisters)
-        clobbers.set(info.sizeRegister);
-
-    B3::PatchpointValue* patchpoint = block->appendNew<B3::PatchpointValue>(proc, B3::Void, Origin());
-    patchpoint->effects = Effects::none();
-    patchpoint->effects.writesPinned = true;
-    patchpoint->clobber(clobbers);
-    patchpoint->numGPScratchRegisters = 1;
-
-    patchpoint->append(instance, ValueRep::SomeRegister);
-
-    patchpoint->setGenerator([memory] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
-        AllowMacroScratchRegisterUsage allowScratch(jit);
-
-        GPRReg scratch = params.gpScratch(0);
-        jit.loadPtr(CCallHelpers::Address(params[0].gpr(), JSWebAssemblyInstance::offsetOfMemory()), scratch);
-        jit.loadPtr(CCallHelpers::Address(scratch, JSWebAssemblyMemory::offsetOfMemory()), scratch);
-        jit.loadPtr(CCallHelpers::Address(scratch, Memory::offsetOfMemory()), memory->pinnedRegisters().baseMemoryPointer);
-
-        jit.load64(CCallHelpers::Address(scratch, Memory::offsetOfSize()), scratch);
-        for (unsigned i = 0; i < memory->pinnedRegisters().sizeRegisters.size(); i++) {
-            GPRReg sizeReg = memory->pinnedRegisters().sizeRegisters[i].sizeRegister;
-            jit.move(scratch, sizeReg);
-            jit.sub64(CCallHelpers::TrustedImm32(memory->pinnedRegisters().sizeRegisters[i].sizeOffset), sizeReg);
-        }
-    });
-}
-
-void B3IRGenerator::emitReloadPinnedRegisters()
-{
-    reloadPinnedRegisters(m_proc, m_currentBlock, m_info, m_instanceValue);
-}
-
-
 auto B3IRGenerator::addGrowMemory(ExpressionType delta, ExpressionType& result) -> PartialResult
 {
     int32_t (*growMemory) (ExecState*, int32_t) = [] (ExecState* exec, int32_t delta) -> int32_t {
@@ -389,7 +394,6 @@ auto B3IRGenerator::addGrowMemory(ExpressionType delta, ExpressionType& result)
 
         JSWebAssemblyInstance* instance = vm.topJSWebAssemblyInstance;
         JSWebAssemblyMemory* wasmMemory = instance->memory();
-        RELEASE_ASSERT(wasmMemory); // This would fail validation otherwise.
 
         if (delta < 0)
             return -1;
@@ -407,21 +411,19 @@ auto B3IRGenerator::addGrowMemory(ExpressionType delta, ExpressionType& result)
         m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), bitwise_cast<void*>(growMemory)),
         m_currentBlock->appendNew<B3::Value>(m_proc, B3::FramePointer, Origin()), delta);
 
-    emitReloadPinnedRegisters();
+    restoreWebAssemblyGlobalState(m_vm, m_info.memory, m_instanceValue, m_proc, m_currentBlock);
 
     return { };
 }
 
 auto B3IRGenerator::addCurrentMemory(ExpressionType& result) -> PartialResult
 {
-    Value* jsMemory = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), m_instanceValue, JSWebAssemblyInstance::offsetOfMemory());
-    Value* wasmMemory = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), jsMemory, JSWebAssemblyMemory::offsetOfMemory());
-    Value* size = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, B3::Int64, Origin(), wasmMemory, Memory::offsetOfSize());
+    auto memoryValue = getMemoryBaseAndSize(m_vm, m_instanceValue, m_proc, m_currentBlock);
 
     constexpr uint32_t shiftValue = 16;
     static_assert(PageCount::pageSize == 1 << shiftValue, "This must hold for the code below to be correct.");
     Value* numPages = m_currentBlock->appendNew<Value>(m_proc, ZShr, Origin(),
-        size, m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), shiftValue));
+        memoryValue.size, m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), shiftValue));
 
     result = m_currentBlock->appendNew<Value>(m_proc, Trunc, Origin(), numPages);
 
@@ -832,26 +834,75 @@ auto B3IRGenerator::addCall(uint32_t functionIndex, const Signature* signature,
     ASSERT(signature->argumentCount() == args.size());
 
     Type returnType = signature->returnType();
+    Vector<UnlinkedWasmToWasmCall>* unlinkedWasmToWasmCalls = &m_unlinkedWasmToWasmCalls;
 
-    result = wasmCallingConvention().setupCall(m_proc, m_currentBlock, Origin(), args, toB3Type(returnType),
-        [&] (PatchpointValue* patchpoint) {
-            patchpoint->effects.writesPinned = true;
-            patchpoint->effects.readsPinned = true;
+    if (m_info.isImportedFunctionFromFunctionIndexSpace(functionIndex)) {
+        // FIXME imports can be linked here, instead of generating a patchpoint, because all import stubs are generated before B3 compilation starts. https://bugs.webkit.org/show_bug.cgi?id=166462
+        Value* functionImport = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), m_instanceValue, JSWebAssemblyInstance::offsetOfImportFunction(functionIndex));
+        Value* jsTypeOfImport = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, Origin(), functionImport, JSCell::typeInfoTypeOffset());
+        Value* isWasmCall = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), jsTypeOfImport, m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), WebAssemblyFunctionType));
 
-            Vector<UnlinkedWasmToWasmCall>* unlinkedWasmToWasmCalls = &m_unlinkedWasmToWasmCalls;
-            patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
-                AllowMacroScratchRegisterUsage allowScratch(jit);
+        BasicBlock* isWasmBlock = m_proc.addBlock();
+        BasicBlock* isJSBlock = m_proc.addBlock();
+        BasicBlock* continuation = m_proc.addBlock();
+        m_currentBlock->appendNewControlValue(m_proc, B3::Branch, Origin(), isWasmCall, FrequentedBlock(isWasmBlock), FrequentedBlock(isJSBlock));
+
+        Value* wasmCallResult = wasmCallingConvention().setupCall(m_proc, isWasmBlock, Origin(), args, toB3Type(returnType),
+            [&] (PatchpointValue* patchpoint) {
+                patchpoint->effects.writesPinned = true;
+                patchpoint->effects.readsPinned = true;
+                patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
+                    AllowMacroScratchRegisterUsage allowScratch(jit);
+                    CCallHelpers::Call call = jit.call();
+                    jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndex] (LinkBuffer& linkBuffer) {
+                        unlinkedWasmToWasmCalls->append({ linkBuffer.locationOf(call), functionIndex, UnlinkedWasmToWasmCall::Target::ToWasm });
+                    });
+                });
+            });
+        UpsilonValue* wasmCallResultUpsilon = returnType == Void ? nullptr : isWasmBlock->appendNew<UpsilonValue>(m_proc, Origin(), wasmCallResult);
+        isWasmBlock->appendNewControlValue(m_proc, Jump, Origin(), continuation);
+
+        Value* jsCallResult = wasmCallingConvention().setupCall(m_proc, isJSBlock, Origin(), args, toB3Type(returnType),
+            [&] (PatchpointValue* patchpoint) {
+                patchpoint->effects.writesPinned = true;
+                patchpoint->effects.readsPinned = true;
+                patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
+                    AllowMacroScratchRegisterUsage allowScratch(jit);
+                    CCallHelpers::Call call = jit.call();
+                    jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndex] (LinkBuffer& linkBuffer) {
+                        unlinkedWasmToWasmCalls->append({ linkBuffer.locationOf(call), functionIndex, UnlinkedWasmToWasmCall::Target::ToJs });
+                    });
+                });
+            });
+        UpsilonValue* jsCallResultUpsilon = returnType == Void ? nullptr : isJSBlock->appendNew<UpsilonValue>(m_proc, Origin(), jsCallResult);
+        isJSBlock->appendNewControlValue(m_proc, Jump, Origin(), continuation);
+
+        m_currentBlock = continuation;
 
-                CCallHelpers::Call call = jit.call();
+        if (returnType == Void)
+            result = nullptr;
+        else {
+            result = continuation->appendNew<Value>(m_proc, Phi, toB3Type(returnType), Origin());
+            wasmCallResultUpsilon->setPhi(result);
+            jsCallResultUpsilon->setPhi(result);
+        }
 
-                jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndex] (LinkBuffer& linkBuffer) {
-                    unlinkedWasmToWasmCalls->append({ linkBuffer.locationOf(call), functionIndex });
+        // The call could have been to another WebAssembly instance, and / or could have modified our Memory.
+        restoreWebAssemblyGlobalState(m_vm, m_info.memory, m_instanceValue, m_proc, continuation);
+    } else {
+        result = wasmCallingConvention().setupCall(m_proc, m_currentBlock, Origin(), args, toB3Type(returnType),
+            [&] (PatchpointValue* patchpoint) {
+                patchpoint->effects.writesPinned = true;
+                patchpoint->effects.readsPinned = true;
+                patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
+                    AllowMacroScratchRegisterUsage allowScratch(jit);
+                    CCallHelpers::Call call = jit.call();
+                    jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndex] (LinkBuffer& linkBuffer) {
+                        unlinkedWasmToWasmCalls->append({ linkBuffer.locationOf(call), functionIndex, UnlinkedWasmToWasmCall::Target::ToWasm });
+                    });
                 });
             });
-        });
-
-    if (functionIndex < m_info.importFunctionCount())
-        emitReloadPinnedRegisters();
+    }
 
     return { };
 }
@@ -929,7 +980,8 @@ auto B3IRGenerator::addCallIndirect(const Signature* signature, SignatureIndex s
             });
         });
 
-    emitReloadPinnedRegisters();
+    // The call could have been to another WebAssembly instance, and / or could have modified our Memory.
+    restoreWebAssemblyGlobalState(m_vm, m_info.memory, m_instanceValue, m_proc, m_currentBlock);
 
     return { };
 }
@@ -998,12 +1050,10 @@ static void createJSToWasmWrapper(VM& vm, CompilationContext& compilationContext
         });
     }
 
-    // Move memory values to the approriate places, if needed.
-    {
-        Value* instance = block->appendNew<MemoryValue>(proc, Load, pointerType(), Origin(),
-            block->appendNew<ConstPtrValue>(proc, Origin(), &vm.topJSWebAssemblyInstance));
-        reloadPinnedRegisters(proc, block, info, instance);
-    }
+    // FIXME The instance is currently set by the C++ code in WebAssemblyFunction::call. We shouldn't go through the extra C++ hoop. https://bugs.webkit.org/show_bug.cgi?id=166486
+    Value* instance = block->appendNew<MemoryValue>(proc, Load, pointerType(), Origin(),
+        block->appendNew<ConstPtrValue>(proc, Origin(), &vm.topJSWebAssemblyInstance));
+    restoreWebAssemblyGlobalState(vm, info.memory, instance, proc, block);
 
     // Get our arguments.
     Vector<Value*> arguments;
@@ -1046,7 +1096,7 @@ static void createJSToWasmWrapper(VM& vm, CompilationContext& compilationContext
     function.jsToWasmEntrypoint.calleeSaveRegisters = proc.calleeSaveRegisters();
 }
 
-Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM& vm, CompilationContext& compilationContext, const uint8_t* functionStart, size_t functionLength, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& info, unsigned optLevel)
+Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM& vm, CompilationContext& compilationContext, const uint8_t* functionStart, size_t functionLength, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ModuleInformation& info, const Vector<SignatureIndex>& moduleSignatureIndicesToUniquedSignatureIndices, unsigned optLevel)
 {
     auto result = std::make_unique<WasmInternalFunction>();
 
@@ -1054,8 +1104,8 @@ Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM& vm,
     compilationContext.wasmEntrypointJIT = std::make_unique<CCallHelpers>(&vm);
 
     Procedure procedure;
-    B3IRGenerator context(vm, info, procedure, result.get(), unlinkedWasmToWasmCalls, functionIndexSpace);
-    FunctionParser<B3IRGenerator> parser(&vm, context, functionStart, functionLength, signature, functionIndexSpace, info);
+    B3IRGenerator context(vm, info, procedure, result.get(), unlinkedWasmToWasmCalls);
+    FunctionParser<B3IRGenerator> parser(&vm, context, functionStart, functionLength, signature, info, moduleSignatureIndicesToUniquedSignatureIndices);
     WASM_FAIL_IF_HELPER_FAILS(parser.parse());
 
     procedure.resetReachability();
index b5c9f46..249d578 100644 (file)
@@ -47,7 +47,7 @@ struct CompilationContext {
     CCallHelpers::Call jsEntrypointToWasmEntrypointCall;
 };
 
-Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM&, CompilationContext&, const uint8_t*, size_t, const Signature*, Vector<UnlinkedWasmToWasmCall>&, const ImmutableFunctionIndexSpace&, const ModuleInformation&, unsigned optLevel = 1);
+Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM&, CompilationContext&, const uint8_t*, size_t, const Signature*, Vector<UnlinkedWasmToWasmCall>&, const ModuleInformation&, const Vector<SignatureIndex>&, unsigned optLevel = 1);
 
 } } // namespace JSC::Wasm
 
index 0e59247..99702ec 100644 (file)
 
 namespace JSC { namespace Wasm {
 
-WasmToJSStub importStubGenerator(VM* vm, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex signatureIndex, unsigned importIndex)
+typedef AssemblyHelpers JIT;
+
+static void materializeImportJSCell(VM* vm, JIT& jit, unsigned importIndex, GPRReg result)
+{
+    // We're calling out of the current WebAssembly.Instance, which is identified on VM. That Instance has a list of all its import functions.
+    jit.loadPtr(&vm->topJSWebAssemblyInstance, result);
+    jit.loadPtr(JIT::Address(result, JSWebAssemblyInstance::offsetOfImportFunction(importIndex)), result);
+}
+
+static MacroAssemblerCodeRef wasmToJs(VM* vm, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex signatureIndex, unsigned importIndex)
 {
     const WasmCallingConvention& wasmCC = wasmCallingConvention();
     const JSCCallingConvention& jsCC = jscCallingConvention();
     const Signature* signature = SignatureInformation::get(vm, signatureIndex);
     unsigned argCount = signature->argumentCount();
-    typedef AssemblyHelpers JIT;
     JIT jit(vm, nullptr);
 
     // Below, we assume that the JS calling convention is always on the stack.
@@ -138,9 +146,7 @@ WasmToJSStub importStubGenerator(VM* vm, Bag<CallLinkInfo>& callLinkInfos, Signa
     GPRReg importJSCellGPRReg = GPRInfo::regT0; // Callee needs to be in regT0 for slow path below.
     ASSERT(!wasmCC.m_calleeSaveRegisters.get(importJSCellGPRReg));
 
-    // Each JS -> wasm entry sets the WebAssembly.Instance whose export is being called. We're calling out of this Instance, and can therefore figure out the import being called.
-    jit.loadPtr(&vm->topJSWebAssemblyInstance, importJSCellGPRReg);
-    jit.loadPtr(JIT::Address(importJSCellGPRReg, JSWebAssemblyInstance::offsetOfImportFunction(importIndex)), importJSCellGPRReg);
+    materializeImportJSCell(vm, jit, importIndex, importJSCellGPRReg);
 
     uint64_t thisArgument = ValueUndefined; // FIXME what does the WebAssembly spec say this should be? https://bugs.webkit.org/show_bug.cgi?id=165471
     jit.store64(importJSCellGPRReg, calleeFrame.withOffset(CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
@@ -231,7 +237,56 @@ WasmToJSStub importStubGenerator(VM* vm, Bag<CallLinkInfo>& callLinkInfos, Signa
     CodeLocationLabel hotPathBegin(patchBuffer.locationOf(targetToCheck));
     CodeLocationNearCall hotPathOther = patchBuffer.locationOfNearCall(fastCall);
     callLinkInfo->setCallLocations(callReturnLocation, hotPathBegin, hotPathOther);
-    return FINALIZE_CODE(patchBuffer, ("WebAssembly import[%i] stub for signature %i", importIndex, signatureIndex));
+    String signatureDescription = SignatureInformation::get(vm, signatureIndex)->toString();
+    return FINALIZE_CODE(patchBuffer, ("WebAssembly->JavaScript import[%i] %s", importIndex, signatureDescription.ascii().data()));
+}
+
+static MacroAssemblerCodeRef wasmToWasm(VM* vm, unsigned importIndex)
+{
+    const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
+    JIT jit(vm, nullptr);
+
+    GPRReg scratch = GPRInfo::nonPreservedNonArgumentGPR;
+
+    // B3's call codegen ensures that the JSCell is a WebAssemblyFunction.
+    materializeImportJSCell(vm, jit, importIndex, scratch);
+
+    // Get the callee's WebAssembly.Instance and set it as vm.topJSWebAssemblyInstance. The caller will take care of restoring its own Instance.
+    GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
+    ASSERT(baseMemory != scratch);
+    jit.loadPtr(JIT::Address(scratch, WebAssemblyFunction::offsetOfInstance()), baseMemory); // Instance*.
+    jit.storePtr(baseMemory, &vm->topJSWebAssemblyInstance);
+
+    // FIXME the following code assumes that all WebAssembly.Instance have the same pinned registers. https://bugs.webkit.org/show_bug.cgi?id=162952
+    // Set up the callee's baseMemory register as well as the memory size registers.
+    jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyInstance::offsetOfMemory()), baseMemory); // JSWebAssemblyMemory*.
+    const auto& sizeRegs = pinnedRegs.sizeRegisters;
+    ASSERT(sizeRegs.size() >= 1);
+    ASSERT(sizeRegs[0].sizeRegister != baseMemory);
+    ASSERT(sizeRegs[0].sizeRegister != scratch);
+    ASSERT(!sizeRegs[0].sizeOffset); // The following code assumes we start at 0, and calculates subsequent size registers relative to 0.
+    jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyMemory::offsetOfSize()), sizeRegs[0].sizeRegister); // Memory size.
+    jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyMemory::offsetOfMemory()), baseMemory); // WasmMemory::void*.
+    for (unsigned i = 1; i < sizeRegs.size(); ++i) {
+        ASSERT(sizeRegs[i].sizeRegister != baseMemory);
+        ASSERT(sizeRegs[i].sizeRegister != scratch);
+        jit.add64(JIT::TrustedImm32(-sizeRegs[i].sizeOffset), sizeRegs[0].sizeRegister, sizeRegs[i].sizeRegister);
+    }
+
+    // Tail call into the callee WebAssembly function.
+    jit.loadPtr(JIT::Address(scratch, WebAssemblyFunction::offsetOfWasmEntryPointCode()), scratch);
+    jit.jump(scratch);
+
+    LinkBuffer patchBuffer(*vm, jit, GLOBAL_THUNK_ID);
+    return FINALIZE_CODE(patchBuffer, ("WebAssembly->WebAssembly import[%i]", importIndex));
+}
+
+WasmExitStubs exitStubGenerator(VM* vm, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex signatureIndex, unsigned importIndex)
+{
+    WasmExitStubs stubs;
+    stubs.wasmToJs = wasmToJs(vm, callLinkInfos, signatureIndex, importIndex);
+    stubs.wasmToWasm = wasmToWasm(vm, importIndex);
+    return stubs;
 }
 
 } } // namespace JSC::Wasm
index fd7e03d..4021f17 100644 (file)
@@ -38,7 +38,7 @@ class CallLinkInfo;
 
 namespace Wasm {
 
-WasmToJSStub importStubGenerator(VM*, Bag<CallLinkInfo>&, SignatureIndex, unsigned);
+WasmExitStubs exitStubGenerator(VM*, Bag<CallLinkInfo>&, SignatureIndex, unsigned);
 
 } } // namespace JSC::Wasm
 
index cb2be55..0d7e0a5 100644 (file)
@@ -222,7 +222,6 @@ private:
 };
 
 struct ModuleInformation {
-    Vector<SignatureIndex> signatureIndices;
     Vector<Import> imports;
     Vector<SignatureIndex> importFunctionSignatureIndices;
     Vector<SignatureIndex> internalFunctionSignatureIndices;
@@ -236,6 +235,18 @@ struct ModuleInformation {
     TableInformation tableInformation;
     Vector<Global> globals;
     unsigned firstInternalGlobal { 0 };
+    size_t functionIndexSpaceSize() const { return importFunctionSignatureIndices.size() + internalFunctionSignatureIndices.size(); }
+    bool isImportedFunctionFromFunctionIndexSpace(size_t functionIndex) const
+    {
+        ASSERT(functionIndex < functionIndexSpaceSize());
+        return functionIndex < importFunctionSignatureIndices.size();
+    }
+    SignatureIndex signatureIndexFromFunctionIndexSpace(size_t functionIndex) const
+    {
+        return isImportedFunctionFromFunctionIndexSpace(functionIndex)
+            ? importFunctionSignatureIndices[functionIndex]
+            : internalFunctionSignatureIndices[functionIndex - importFunctionSignatureIndices.size()];
+    }
 
     uint32_t importFunctionCount() const { return importFunctionSignatureIndices.size(); }
     bool hasMemory() const { return !!memory; }
@@ -246,6 +257,10 @@ struct ModuleInformation {
 struct UnlinkedWasmToWasmCall {
     CodeLocationCall callLocation;
     size_t functionIndex;
+    enum class Target : uint8_t {
+        ToJs,
+        ToWasm,
+    } target;
 };
 
 struct Entrypoint {
@@ -261,7 +276,10 @@ struct WasmInternalFunction {
     Entrypoint jsToWasmEntrypoint;
 };
 
-typedef MacroAssemblerCodeRef WasmToJSStub;
+struct WasmExitStubs {
+    MacroAssemblerCodeRef wasmToJs;
+    MacroAssemblerCodeRef wasmToWasm;
+};
 
 // WebAssembly direct calls and call_indirect use indices into "function index space". This space starts with all imports, and then all internal functions.
 // CallableFunction and FunctionIndexSpace are only meant as fast lookup tables for these opcodes, and do not own code.
@@ -280,12 +298,6 @@ struct CallableFunction {
 };
 typedef Vector<CallableFunction> FunctionIndexSpace;
 
-
-struct ImmutableFunctionIndexSpace {
-    MallocPtr<CallableFunction> buffer;
-    size_t size;
-};
-
 } } // namespace JSC::Wasm
 
 #endif // ENABLE(WEBASSEMBLY)
index 52010dc..31105d8 100644 (file)
@@ -46,7 +46,7 @@ public:
     typedef typename Context::ControlType ControlType;
     typedef typename Context::ExpressionList ExpressionList;
 
-    FunctionParser(VM*, Context&, const uint8_t* functionStart, size_t functionLength, const Signature*, const ImmutableFunctionIndexSpace&, const ModuleInformation&);
+    FunctionParser(VM*, Context&, const uint8_t* functionStart, size_t functionLength, const Signature*, const ModuleInformation&, const Vector<SignatureIndex>&);
 
     Result WARN_UNUSED_RETURN parse();
 
@@ -82,18 +82,18 @@ private:
     ExpressionList m_expressionStack;
     Vector<ControlEntry> m_controlStack;
     const Signature* m_signature;
-    const ImmutableFunctionIndexSpace& m_functionIndexSpace;
     const ModuleInformation& m_info;
+    const Vector<SignatureIndex>& m_moduleSignatureIndicesToUniquedSignatureIndices;
     unsigned m_unreachableBlocks { 0 };
 };
 
 template<typename Context>
-FunctionParser<Context>::FunctionParser(VM* vm, Context& context, const uint8_t* functionStart, size_t functionLength, const Signature* signature, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& info)
+FunctionParser<Context>::FunctionParser(VM* vm, Context& context, const uint8_t* functionStart, size_t functionLength, const Signature* signature, const ModuleInformation& info, const Vector<SignatureIndex>& moduleSignatureIndicesToUniquedSignatureIndices)
     : Parser(vm, functionStart, functionLength)
     , m_context(context)
     , m_signature(signature)
-    , m_functionIndexSpace(functionIndexSpace)
     , m_info(info)
+    , m_moduleSignatureIndicesToUniquedSignatureIndices(moduleSignatureIndicesToUniquedSignatureIndices)
 {
     if (verbose)
         dataLogLn("Parsing function starting at: ", (uintptr_t)functionStart, " of length: ", functionLength);
@@ -308,9 +308,9 @@ auto FunctionParser<Context>::parseExpression(OpType op) -> PartialResult
     case Call: {
         uint32_t functionIndex;
         WASM_PARSER_FAIL_IF(!parseVarUInt32(functionIndex), "can't parse call's function index");
-        WASM_PARSER_FAIL_IF(functionIndex >= m_functionIndexSpace.size, "call function index ", functionIndex, " exceeds function index space ", m_functionIndexSpace.size);
+        WASM_PARSER_FAIL_IF(functionIndex >= m_info.functionIndexSpaceSize(), "call function index ", functionIndex, " exceeds function index space ", m_info.functionIndexSpaceSize());
 
-        SignatureIndex calleeSignatureIndex = m_functionIndexSpace.buffer.get()[functionIndex].signatureIndex;
+        SignatureIndex calleeSignatureIndex = m_info.signatureIndexFromFunctionIndexSpace(functionIndex);
         const Signature* calleeSignature = SignatureInformation::get(m_vm, calleeSignatureIndex);
         WASM_PARSER_FAIL_IF(calleeSignature->argumentCount() > m_expressionStack.size(), "call function index ", functionIndex, " has ", calleeSignature->argumentCount(), " arguments, but the expression stack currently holds ", m_expressionStack.size(), " values");
 
@@ -337,9 +337,9 @@ auto FunctionParser<Context>::parseExpression(OpType op) -> PartialResult
         WASM_PARSER_FAIL_IF(!parseVarUInt32(signatureIndex), "can't get call_indirect's signature index");
         WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't get call_indirect's reserved byte");
         WASM_PARSER_FAIL_IF(reserved, "call_indirect's 'reserved' varuint1 must be 0x0");
-        WASM_PARSER_FAIL_IF(m_info.signatureIndices.size() <= signatureIndex, "call_indirect's signature index ", signatureIndex, " exceeds known signatures ", m_info.signatureIndices.size());
+        WASM_PARSER_FAIL_IF(m_moduleSignatureIndicesToUniquedSignatureIndices.size() <= signatureIndex, "call_indirect's signature index ", signatureIndex, " exceeds known signatures ", m_moduleSignatureIndicesToUniquedSignatureIndices.size());
 
-        SignatureIndex calleeSignatureIndex = m_info.signatureIndices[signatureIndex];
+        SignatureIndex calleeSignatureIndex = m_moduleSignatureIndicesToUniquedSignatureIndices[signatureIndex];
         const Signature* calleeSignature = SignatureInformation::get(m_vm, calleeSignatureIndex);
         size_t argumentCount = calleeSignature->argumentCount() + 1; // Add the callee's index.
         WASM_PARSER_FAIL_IF(argumentCount > m_expressionStack.size(), "call_indirect expects ", argumentCount, " arguments, but the expression stack currently holds ", m_expressionStack.size(), " values");
index 700e3ac..aad91c1 100644 (file)
 
 #if ENABLE(WEBASSEMBLY)
 
+#include <wtf/HexNumber.h>
+#include <wtf/PrintStream.h>
+#include <wtf/text/WTFString.h>
+
 namespace JSC { namespace Wasm {
 
+namespace {
+const bool verbose = false;
+}
+
+void Memory::dump(PrintStream& out) const
+{
+    String memoryHex;
+    WTF::appendUnsigned64AsHex((uint64_t)(uintptr_t)m_memory, memoryHex);
+    out.print("Memory at 0x", memoryHex, ", size ", m_size, "B capacity ", m_mappedCapacity, "B, initial ", m_initial, " maximum ", m_maximum, " mode ", makeString(m_mode));
+}
+
+const char* Memory::makeString(Mode mode) const
+{
+    switch (mode) {
+    case Mode::BoundsChecking: return "BoundsChecking";
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+    return "";
+}
+
 static_assert(sizeof(uint64_t) == sizeof(size_t), "We rely on allowing the maximum size of Memory we map to be 2^32 which is larger than fits in a 32-bit integer that we'd pass to mprotect if this didn't hold.");
 
 Memory::Memory(PageCount initial, PageCount maximum, bool& failed)
-    : m_mode(Mode::BoundsChecking)
+    : m_size(initial.bytes())
     , m_initial(initial)
     , m_maximum(maximum)
-    , m_size(initial.bytes())
+    , m_mode(Mode::BoundsChecking)
     // FIXME: If we add signal based bounds checking then we need extra space for overflow on load.
     // see: https://bugs.webkit.org/show_bug.cgi?id=162693
 {
@@ -46,9 +70,11 @@ Memory::Memory(PageCount initial, PageCount maximum, bool& failed)
     if (!m_mappedCapacity) {
         // This means we specified a zero as maximum (which means we also have zero as initial size).
         RELEASE_ASSERT(m_size == 0);
-        m_mappedCapacity = 0;
         m_memory = nullptr;
+        m_mappedCapacity = 0;
         failed = false;
+        if (verbose)
+            dataLogLn("Memory::Memory allocating nothing ", *this);
         return;
     }
 
@@ -56,15 +82,21 @@ Memory::Memory(PageCount initial, PageCount maximum, bool& failed)
     void* result = Options::simulateWebAssemblyLowMemory() ? MAP_FAILED : mmap(nullptr, m_mappedCapacity, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
     if (result == MAP_FAILED) {
         // Try again with a different number.
+        if (verbose)
+            dataLogLn("Memory::Memory mmap failed once for capacity, trying again", *this);
         m_mappedCapacity = m_size;
         if (!m_mappedCapacity) {
             m_memory = nullptr;
             failed = false;
+            if (verbose)
+                dataLogLn("Memory::Memory mmap not trying again because size is zero ", *this);
             return;
         }
 
         result = mmap(nullptr, m_mappedCapacity, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
         if (result == MAP_FAILED) {
+            if (verbose)
+                dataLogLn("Memory::Memory mmap failed twice ", *this);
             failed = true;
             return;
         }
@@ -78,12 +110,27 @@ Memory::Memory(PageCount initial, PageCount maximum, bool& failed)
 
     m_memory = result;
     failed = false;
+    if (verbose)
+        dataLogLn("Memory::Memory mmap succeeded ", *this);
+}
+
+Memory::~Memory()
+{
+    if (verbose)
+        dataLogLn("Memory::~Memory ", *this);
+    if (m_memory) {
+        if (munmap(m_memory, m_mappedCapacity))
+            CRASH();
+    }
 }
 
 bool Memory::grow(PageCount newSize)
 {
     RELEASE_ASSERT(newSize > PageCount::fromBytes(m_size));
 
+    if (verbose)
+        dataLogLn("Memory::grow to ", newSize, " from ", *this);
+
     if (maximum() && newSize > maximum())
         return false;
 
@@ -93,6 +140,8 @@ bool Memory::grow(PageCount newSize)
         bool success = !mprotect(static_cast<uint8_t*>(m_memory) + m_size, static_cast<size_t>(desiredSize - m_size), PROT_READ | PROT_WRITE);
         RELEASE_ASSERT(success);
         m_size = desiredSize;
+        if (verbose)
+            dataLogLn("Memory::grow in-place ", *this);
         return true;
     }
 
@@ -110,6 +159,8 @@ bool Memory::grow(PageCount newSize)
     m_mappedCapacity = desiredSize;
     m_size = desiredSize;
 
+    if (verbose)
+        dataLogLn("Memory::grow ", *this);
     return true;
 }
 
index 4e5ee36..236bf85 100644 (file)
@@ -30,7 +30,9 @@
 #include "WasmCallingConvention.h"
 #include "WasmPageCount.h"
 
-#include <wtf/Vector.h>
+namespace WTF {
+class PrintStream;
+}
 
 namespace JSC { namespace Wasm {
 
@@ -38,41 +40,50 @@ class Memory {
     WTF_MAKE_NONCOPYABLE(Memory);
     WTF_MAKE_FAST_ALLOCATED;
 public:
+    void dump(WTF::PrintStream&) const;
 
     // FIXME: We should support other modes. see: https://bugs.webkit.org/show_bug.cgi?id=162693
     enum class Mode {
         BoundsChecking
     };
+    const char* makeString(Mode) const;
 
+    Memory() = default;
     JS_EXPORT_PRIVATE Memory(PageCount initial, PageCount maximum, bool& failed);
-
-    ~Memory()
+    Memory(Memory&& other)
+        : m_memory(other.m_memory)
+        , m_size(other.m_size)
+        , m_initial(other.m_initial)
+        , m_maximum(other.m_maximum)
+        , m_mappedCapacity(other.m_mappedCapacity)
+        , m_mode(other.m_mode)
     {
-        if (m_memory)
-            munmap(m_memory, m_mappedCapacity);
+        // Moving transfers ownership of the allocated memory.
+        other.m_memory = nullptr;
     }
+    ~Memory();
 
     void* memory() const { return m_memory; }
     uint64_t size() const { return m_size; }
     PageCount sizeInPages() const { return PageCount::fromBytes(m_size); }
 
-    Mode mode() const { return m_mode; }
-
     PageCount initial() const { return m_initial; }
     PageCount maximum() const { return m_maximum; }
 
+    Mode mode() const { return m_mode; }
+
     bool grow(PageCount);
 
-    static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(Memory, m_size); }
     static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(Memory, m_memory); }
+    static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(Memory, m_size); }
     
 private:
     void* m_memory { nullptr };
-    Mode m_mode;
+    uint64_t m_size { 0 };
     PageCount m_initial;
     PageCount m_maximum;
-    uint64_t m_size { 0 };
     uint64_t m_mappedCapacity { 0 };
+    Mode m_mode { Mode::BoundsChecking };
 };
 
 } } // namespace JSC::Wasm
index 2e1d30a..6b063d3 100644 (file)
 #include "config.h"
 #include "WasmMemoryInformation.h"
 
-#include "WasmCallingConvention.h"
-
 #if ENABLE(WEBASSEMBLY)
 
+#include "WasmCallingConvention.h"
+#include <wtf/NeverDestroyed.h>
+
 namespace JSC { namespace Wasm {
 
-MemoryInformation::MemoryInformation(PageCount initial, PageCount maximum,  const Vector<unsigned>& pinnedSizeRegisters, bool isImport)
+const PinnedRegisterInfo& PinnedRegisterInfo::get()
+{
+    static LazyNeverDestroyed<PinnedRegisterInfo> staticPinnedRegisterInfo;
+    static std::once_flag staticPinnedRegisterInfoFlag;
+    std::call_once(staticPinnedRegisterInfoFlag, [] () {
+        Vector<PinnedSizeRegisterInfo> sizeRegisters;
+        GPRReg baseMemoryPointer;
+
+        // FIXME: We should support more than one memory size register, and we should allow different
+        //        WebAssembly.Instance to have different pins. Right now we take a vector with only one entry.
+        //        If we have more than one size register, we can have one for each load size class.
+        //        see: https://bugs.webkit.org/show_bug.cgi?id=162952
+        Vector<unsigned> pinnedSizes = { 0 };
+        unsigned remainingPinnedRegisters = pinnedSizes.size() + 1;
+        jscCallingConvention().m_calleeSaveRegisters.forEach([&] (Reg reg) {
+            GPRReg gpr = reg.gpr();
+            if (!remainingPinnedRegisters || RegisterSet::stackRegisters().get(reg))
+                return;
+            if (remainingPinnedRegisters == 1) {
+                baseMemoryPointer = gpr;
+                remainingPinnedRegisters--;
+            } else
+                sizeRegisters.append({ gpr, pinnedSizes[--remainingPinnedRegisters - 1] });
+        });
+
+        ASSERT(!remainingPinnedRegisters);
+        staticPinnedRegisterInfo.construct(WTFMove(sizeRegisters), baseMemoryPointer);
+    });
+
+    return staticPinnedRegisterInfo.get();
+}
+
+PinnedRegisterInfo::PinnedRegisterInfo(Vector<PinnedSizeRegisterInfo>&& sizeRegisters, GPRReg baseMemoryPointer)
+    : sizeRegisters(WTFMove(sizeRegisters))
+    , baseMemoryPointer(baseMemoryPointer)
+{
+}
+
+MemoryInformation::MemoryInformation(PageCount initial, PageCount maximum,  bool isImport)
     : m_initial(initial)
     , m_maximum(maximum)
     , m_isImport(isImport)
@@ -40,20 +79,6 @@ MemoryInformation::MemoryInformation(PageCount initial, PageCount maximum,  cons
     RELEASE_ASSERT(!!m_initial);
     RELEASE_ASSERT(!m_maximum || m_maximum >= m_initial);
     ASSERT(!!*this);
-
-    unsigned remainingPinnedRegisters = pinnedSizeRegisters.size() + 1;
-    jscCallingConvention().m_calleeSaveRegisters.forEach([&] (Reg reg) {
-        GPRReg gpr = reg.gpr();
-        if (!remainingPinnedRegisters || RegisterSet::stackRegisters().get(reg))
-            return;
-        if (remainingPinnedRegisters == 1) {
-            m_pinnedRegisters.baseMemoryPointer = gpr;
-            remainingPinnedRegisters--;
-        } else
-            m_pinnedRegisters.sizeRegisters.append({ gpr, pinnedSizeRegisters[--remainingPinnedRegisters - 1] });
-    });
-
-    ASSERT(!remainingPinnedRegisters);
 }
 
 } } // namespace JSC::Wasm
index ee63d9e..6e7f521 100644 (file)
@@ -38,12 +38,11 @@ struct PinnedSizeRegisterInfo {
     unsigned sizeOffset;
 };
 
-// FIXME: We should support more than one memory size register. Right now we take a vector with only one
-// entry. Specifically an entry where the sizeOffset == 0. If we have more than one size register,
-// we can have one for each load size class. see: https://bugs.webkit.org/show_bug.cgi?id=162952
 struct PinnedRegisterInfo {
     Vector<PinnedSizeRegisterInfo> sizeRegisters;
     GPRReg baseMemoryPointer;
+    static const PinnedRegisterInfo& get();
+    PinnedRegisterInfo(Vector<PinnedSizeRegisterInfo>&&, GPRReg);
 };
 
 class MemoryInformation {
@@ -53,9 +52,8 @@ public:
         ASSERT(!*this);
     }
 
-    MemoryInformation(PageCount initial, PageCount maximum, const Vector<unsigned>& pinnedSizeRegisters, bool isImport);
+    MemoryInformation(PageCount initial, PageCount maximum, bool isImport);
 
-    const PinnedRegisterInfo& pinnedRegisters() const { return m_pinnedRegisters; }
     PageCount initial() const { return m_initial; }
     PageCount maximum() const { return m_maximum; }
     bool isImport() const { return m_isImport; }
@@ -65,7 +63,6 @@ public:
 private:
     PageCount m_initial { };
     PageCount m_maximum { };
-    PinnedRegisterInfo m_pinnedRegisters { };
     bool m_isImport { false };
 };
 
index 6261e7f..3011c89 100644 (file)
@@ -107,7 +107,7 @@ auto ModuleParser::parseType() -> PartialResult
 
     WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Type section's count");
     WASM_PARSER_FAIL_IF(count == std::numeric_limits<uint32_t>::max(), "Type section's count is too big ", count);
-    WASM_PARSER_FAIL_IF(!m_result.module->signatureIndices.tryReserveCapacity(count), "can't allocate enough memory for Type section's ", count, " entries");
+    WASM_PARSER_FAIL_IF(!m_result.moduleSignatureIndicesToUniquedSignatureIndices.tryReserveCapacity(count), "can't allocate enough memory for Type section's ", count, " entries");
 
     for (uint32_t i = 0; i < count; ++i) {
         int8_t type;
@@ -139,7 +139,7 @@ auto ModuleParser::parseType() -> PartialResult
         signature->returnType() = returnType;
 
         SignatureIndex signatureIndex = SignatureInformation::adopt(m_vm, signature.release());
-        m_result.module->signatureIndices.uncheckedAppend(signatureIndex);
+        m_result.moduleSignatureIndicesToUniquedSignatureIndices.uncheckedAppend(signatureIndex);
     }
     return { };
 }
@@ -152,7 +152,6 @@ auto ModuleParser::parseImport() -> PartialResult
     WASM_PARSER_FAIL_IF(!m_result.module->globals.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " globals"); // FIXME this over-allocates when we fix the FIXMEs below.
     WASM_PARSER_FAIL_IF(!m_result.module->imports.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " imports"); // FIXME this over-allocates when we fix the FIXMEs below.
     WASM_PARSER_FAIL_IF(!m_result.module->importFunctionSignatureIndices.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " import function signatures"); // FIXME this over-allocates when we fix the FIXMEs below.
-    WASM_PARSER_FAIL_IF(!m_result.functionIndexSpace.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " functions in the index space"); // FIXME this over-allocates when we fix the FIXMEs below. We'll allocate some more here when we know how many functions to expect.
 
     for (uint32_t importNumber = 0; importNumber < importCount; ++importNumber) {
         Import imp;
@@ -174,11 +173,10 @@ auto ModuleParser::parseImport() -> PartialResult
         case ExternalKind::Function: {
             uint32_t functionSignatureIndex;
             WASM_PARSER_FAIL_IF(!parseVarUInt32(functionSignatureIndex), "can't get ", importNumber, "th Import's function signature in module '", moduleString, "' field '", fieldString, "'");
-            WASM_PARSER_FAIL_IF(functionSignatureIndex >= m_result.module->signatureIndices.size(), "invalid function signature for ", importNumber, "th Import, ", functionSignatureIndex, " is out of range of ", m_result.module->signatureIndices.size(), " in module '", moduleString, "' field '", fieldString, "'");
+            WASM_PARSER_FAIL_IF(functionSignatureIndex >= m_result.moduleSignatureIndicesToUniquedSignatureIndices.size(), "invalid function signature for ", importNumber, "th Import, ", functionSignatureIndex, " is out of range of ", m_result.moduleSignatureIndicesToUniquedSignatureIndices.size(), " in module '", moduleString, "' field '", fieldString, "'");
             imp.kindIndex = m_result.module->importFunctionSignatureIndices.size();
-            SignatureIndex signatureIndex = m_result.module->signatureIndices[functionSignatureIndex];
+            SignatureIndex signatureIndex = m_result.moduleSignatureIndicesToUniquedSignatureIndices[functionSignatureIndex];
             m_result.module->importFunctionSignatureIndices.uncheckedAppend(signatureIndex);
-            m_result.functionIndexSpace.uncheckedAppend(signatureIndex);
             break;
         }
         case ExternalKind::Table: {
@@ -220,20 +218,18 @@ auto ModuleParser::parseFunction() -> PartialResult
     WASM_PARSER_FAIL_IF(count == std::numeric_limits<uint32_t>::max(), "Function section's count is too big ", count);
     WASM_PARSER_FAIL_IF(!m_result.module->internalFunctionSignatureIndices.tryReserveCapacity(count), "can't allocate enough memory for ", count, " Function signatures");
     WASM_PARSER_FAIL_IF(!m_result.functionLocationInBinary.tryReserveCapacity(count), "can't allocate enough memory for ", count, "Function locations");
-    WASM_PARSER_FAIL_IF(!m_result.functionIndexSpace.tryReserveCapacity(m_result.functionIndexSpace.size() + count), "can't allocate enough memory for ", count, " more functions in the function index space");
 
     for (uint32_t i = 0; i < count; ++i) {
         uint32_t typeNumber;
         WASM_PARSER_FAIL_IF(!parseVarUInt32(typeNumber), "can't get ", i, "th Function's type number");
-        WASM_PARSER_FAIL_IF(typeNumber >= m_result.module->signatureIndices.size(), i, "th Function type number is invalid ", typeNumber);
+        WASM_PARSER_FAIL_IF(typeNumber >= m_result.moduleSignatureIndicesToUniquedSignatureIndices.size(), i, "th Function type number is invalid ", typeNumber);
 
-        SignatureIndex signatureIndex = m_result.module->signatureIndices[typeNumber];
+        SignatureIndex signatureIndex = m_result.moduleSignatureIndicesToUniquedSignatureIndices[typeNumber];
         // The Code section fixes up start and end.
         size_t start = 0;
         size_t end = 0;
         m_result.module->internalFunctionSignatureIndices.uncheckedAppend(signatureIndex);
         m_result.functionLocationInBinary.uncheckedAppend({ start, end });
-        m_result.functionIndexSpace.uncheckedAppend(signatureIndex);
     }
 
     return { };
@@ -320,8 +316,7 @@ auto ModuleParser::parseMemoryHelper(bool isImport) -> PartialResult
     ASSERT(initialPageCount);
     ASSERT(!maximumPageCount || maximumPageCount >= initialPageCount);
 
-    Vector<unsigned> pinnedSizes = { 0 };
-    m_result.module->memory = MemoryInformation(initialPageCount, maximumPageCount, pinnedSizes, isImport);
+    m_result.module->memory = MemoryInformation(initialPageCount, maximumPageCount, isImport);
     return { };
 }
 
@@ -387,7 +382,7 @@ auto ModuleParser::parseExport() -> PartialResult
         WASM_PARSER_FAIL_IF(!parseVarUInt32(exp.kindIndex), "can't get ", exportNumber, "th Export's kind index, named '", fieldString, "'");
         switch (exp.kind) {
         case ExternalKind::Function: {
-            WASM_PARSER_FAIL_IF(exp.kindIndex >= m_result.functionIndexSpace.size(), exportNumber, "th Export has invalid function number ", exp.kindIndex, " it exceeds the function index space ", m_result.functionIndexSpace.size(), ", named '", fieldString, "'");
+            WASM_PARSER_FAIL_IF(exp.kindIndex >= m_result.module->functionIndexSpaceSize(), exportNumber, "th Export has invalid function number ", exp.kindIndex, " it exceeds the function index space ", m_result.module->functionIndexSpaceSize(), ", named '", fieldString, "'");
             break;
         }
         case ExternalKind::Table: {
@@ -417,8 +412,8 @@ auto ModuleParser::parseStart() -> PartialResult
 {
     uint32_t startFunctionIndex;
     WASM_PARSER_FAIL_IF(!parseVarUInt32(startFunctionIndex), "can't get Start index");
-    WASM_PARSER_FAIL_IF(startFunctionIndex >= m_result.functionIndexSpace.size(), "Start index ", startFunctionIndex, " exceeds function index space ", m_result.functionIndexSpace.size());
-    SignatureIndex signatureIndex = m_result.functionIndexSpace[startFunctionIndex].signatureIndex;
+    WASM_PARSER_FAIL_IF(startFunctionIndex >= m_result.module->functionIndexSpaceSize(), "Start index ", startFunctionIndex, " exceeds function index space ", m_result.module->functionIndexSpaceSize());
+    SignatureIndex signatureIndex = m_result.module->signatureIndexFromFunctionIndexSpace(startFunctionIndex);
     const Signature* signature = SignatureInformation::get(m_vm, signatureIndex);
     WASM_PARSER_FAIL_IF(signature->argumentCount(), "Start function can't have arguments");
     WASM_PARSER_FAIL_IF(signature->returnType() != Void, "Start function can't return a value");
@@ -469,7 +464,7 @@ auto ModuleParser::parseElement() -> PartialResult
         for (unsigned index = 0; index < indexCount; ++index) {
             uint32_t functionIndex;
             WASM_PARSER_FAIL_IF(!parseVarUInt32(functionIndex), "can't get Element section's ", elementNum, "th element's ", index, "th index");
-            WASM_PARSER_FAIL_IF(functionIndex >= m_result.functionIndexSpace.size(), "Element section's ", elementNum, "th element's ", index, "th index is ", functionIndex, " which exceeds the function index space size of ", m_result.functionIndexSpace.size());
+            WASM_PARSER_FAIL_IF(functionIndex >= m_result.module->functionIndexSpaceSize(), "Element section's ", elementNum, "th element's ", index, "th index is ", functionIndex, " which exceeds the function index space size of ", m_result.module->functionIndexSpaceSize());
 
             element.functionIndices.uncheckedAppend(functionIndex);
         }
index e3b75a7..7e026c9 100644 (file)
@@ -36,8 +36,8 @@ namespace JSC { namespace Wasm {
 
 struct ModuleParserResult {
     std::unique_ptr<ModuleInformation> module;
-    FunctionIndexSpace functionIndexSpace;
     Vector<FunctionLocationInBinary> functionLocationInBinary;
+    Vector<SignatureIndex> moduleSignatureIndicesToUniquedSignatureIndices;
 };
 
 class ModuleParser : public Parser<ModuleParserResult> {
diff --git a/Source/JavaScriptCore/wasm/WasmPageCount.cpp b/Source/JavaScriptCore/wasm/WasmPageCount.cpp
new file mode 100644 (file)
index 0000000..4b87705
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WasmPageCount.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+#include <wtf/PrintStream.h>
+#include <wtf/text/WTFString.h>
+
+namespace JSC { namespace Wasm {
+
+void PageCount::dump(PrintStream& out) const
+{
+    out.print(String::number(bytes()), "B");
+}
+
+} // namespace JSC
+
+} // namespace Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
index a15d505..67dbb55 100644 (file)
 
 #if ENABLE(WEBASSEMBLY)
 
+namespace WTF {
+class PrintStream;
+}
+
 namespace JSC { namespace Wasm {
 
 class PageCount {
@@ -42,6 +46,8 @@ public:
         : m_pageCount(pageCount)
     { }
 
+    void dump(WTF::PrintStream&) const;
+
     uint64_t bytes() const { return static_cast<uint64_t>(m_pageCount) * static_cast<uint64_t>(pageSize); }
     uint32_t pageCount() const { return m_pageCount; }
 
index ed28bbc..5980b51 100644 (file)
@@ -76,8 +76,7 @@ bool Plan::parseAndValidateModule()
         }
         m_moduleInformation = WTFMove(parseResult->module);
         m_functionLocationInBinary = WTFMove(parseResult->functionLocationInBinary);
-        m_functionIndexSpace.size = parseResult->functionIndexSpace.size();
-        m_functionIndexSpace.buffer = parseResult->functionIndexSpace.releaseBuffer();
+        m_moduleSignatureIndicesToUniquedSignatureIndices = WTFMove(parseResult->moduleSignatureIndicesToUniquedSignatureIndices);
     }
 
     for (unsigned functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); ++functionIndex) {
@@ -89,7 +88,7 @@ bool Plan::parseAndValidateModule()
         SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
         const Signature* signature = SignatureInformation::get(m_vm, signatureIndex);
 
-        auto validationResult = validateFunction(m_vm, functionStart, functionLength, signature, m_functionIndexSpace, *m_moduleInformation);
+        auto validationResult = validateFunction(m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices);
         if (!validationResult) {
             if (verbose) {
                 for (unsigned i = 0; i < functionLength; ++i)
@@ -127,7 +126,7 @@ void Plan::run()
         return true;
     };
 
-    if (!tryReserveCapacity(m_wasmToJSStubs, m_moduleInformation->importFunctionSignatureIndices.size(), " WebAssembly to JavaScript stubs")
+    if (!tryReserveCapacity(m_wasmExitStubs, m_moduleInformation->importFunctionSignatureIndices.size(), " WebAssembly to JavaScript stubs")
         || !tryReserveCapacity(m_unlinkedWasmToWasmCalls, m_functionLocationInBinary.size(), " unlinked WebAssembly to WebAssembly calls")
         || !tryReserveCapacity(m_wasmInternalFunctions, m_functionLocationInBinary.size(), " WebAssembly functions")
         || !tryReserveCapacity(m_compilationContexts, m_functionLocationInBinary.size(), " compilation contexts"))
@@ -141,12 +140,11 @@ void Plan::run()
         Import* import = &m_moduleInformation->imports[importIndex];
         if (import->kind != ExternalKind::Function)
             continue;
-        unsigned importFunctionIndex = m_wasmToJSStubs.size();
+        unsigned importFunctionIndex = m_wasmExitStubs.size();
         if (verbose)
             dataLogLn("Processing import function number ", importFunctionIndex, ": ", import->module, ": ", import->field);
         SignatureIndex signatureIndex = m_moduleInformation->importFunctionSignatureIndices.at(import->kindIndex);
-        m_wasmToJSStubs.uncheckedAppend(importStubGenerator(m_vm, m_callLinkInfos, signatureIndex, importFunctionIndex));
-        m_functionIndexSpace.buffer.get()[importFunctionIndex].code = m_wasmToJSStubs[importFunctionIndex].code().executableAddress();
+        m_wasmExitStubs.uncheckedAppend(exitStubGenerator(m_vm, m_callLinkInfos, signatureIndex, importFunctionIndex));
     }
 
     m_currentIndex = 0;
@@ -167,12 +165,12 @@ void Plan::run()
             ASSERT(functionLength <= m_sourceLength);
             SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
             const Signature* signature = SignatureInformation::get(m_vm, signatureIndex);
-            unsigned functionIndexSpace = m_wasmToJSStubs.size() + functionIndex;
-            ASSERT_UNUSED(functionIndexSpace, m_functionIndexSpace.buffer.get()[functionIndexSpace].signatureIndex == signatureIndex);
-            ASSERT(validateFunction(m_vm, functionStart, functionLength, signature, m_functionIndexSpace, *m_moduleInformation));
+            unsigned functionIndexSpace = m_wasmExitStubs.size() + functionIndex;
+            ASSERT_UNUSED(functionIndexSpace, m_moduleInformation->signatureIndexFromFunctionIndexSpace(functionIndexSpace) == signatureIndex);
+            ASSERT(validateFunction(m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices));
 
             m_unlinkedWasmToWasmCalls[functionIndex] = Vector<UnlinkedWasmToWasmCall>();
-            auto parseAndCompileResult = parseAndCompile(*m_vm, m_compilationContexts[functionIndex], functionStart, functionLength, signature, m_unlinkedWasmToWasmCalls[functionIndex], m_functionIndexSpace, *m_moduleInformation);
+            auto parseAndCompileResult = parseAndCompile(*m_vm, m_compilationContexts[functionIndex], functionStart, functionLength, signature, m_unlinkedWasmToWasmCalls[functionIndex], *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices);
 
             if (UNLIKELY(!parseAndCompileResult)) {
                 auto locker = holdLock(m_lock);
@@ -209,10 +207,12 @@ void Plan::run()
     for (uint32_t functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); functionIndex++) {
         {
             CompilationContext& context = m_compilationContexts[functionIndex];
+            SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
+            String signatureDescription = SignatureInformation::get(m_vm, signatureIndex)->toString();
             {
                 LinkBuffer linkBuffer(*m_vm, *context.wasmEntrypointJIT, nullptr);
                 m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation =
-                    std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("Wasm function")), WTFMove(context.wasmEntrypointByproducts));
+                    std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("WebAssembly function[%i] %s", functionIndex, signatureDescription.ascii().data())), WTFMove(context.wasmEntrypointByproducts));
             }
 
             {
@@ -220,12 +220,9 @@ void Plan::run()
                 linkBuffer.link(context.jsEntrypointToWasmEntrypointCall, FunctionPtr(m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation->code().executableAddress()));
 
                 m_wasmInternalFunctions[functionIndex]->jsToWasmEntrypoint.compilation =
-                    std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("Wasm JS entrypoint")), WTFMove(context.jsEntrypointByproducts));
+                    std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("JavaScript->WebAssembly entrypoint[%i] %s", functionIndex, signatureDescription.ascii().data())), WTFMove(context.jsEntrypointByproducts));
             }
         }
-
-        unsigned functionIndexSpace = m_wasmToJSStubs.size() + functionIndex;
-        m_functionIndexSpace.buffer.get()[functionIndexSpace].code = m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation->code().executableAddress();
     }
 
     if (verbose || Options::reportCompileTimes()) {
@@ -235,8 +232,19 @@ void Plan::run()
 
     // Patch the call sites for each WebAssembly function.
     for (auto& unlinked : m_unlinkedWasmToWasmCalls) {
-        for (auto& call : unlinked)
-            MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(m_functionIndexSpace.buffer.get()[call.functionIndex].code));
+        for (auto& call : unlinked) {
+            void* executableAddress;
+            if (m_moduleInformation->isImportedFunctionFromFunctionIndexSpace(call.functionIndex)) {
+                // FIXME imports could have been linked in B3, instead of generating a patchpoint. This condition should be replaced by a RELEASE_ASSERT. https://bugs.webkit.org/show_bug.cgi?id=166462
+                executableAddress = call.target == UnlinkedWasmToWasmCall::Target::ToJs
+                    ? m_wasmExitStubs.at(call.functionIndex).wasmToJs.code().executableAddress()
+                    : m_wasmExitStubs.at(call.functionIndex).wasmToWasm.code().executableAddress();
+            } else {
+                ASSERT(call.target != UnlinkedWasmToWasmCall::Target::ToJs);
+                executableAddress = m_wasmInternalFunctions.at(call.functionIndex - m_wasmExitStubs.size())->wasmEntrypoint.compilation->code().executableAddress();
+            }
+            MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(executableAddress));
+        }
     }
 
     m_failed = false;
index 3bb01c8..6980bdb 100644 (file)
@@ -86,26 +86,20 @@ public:
         return WTFMove(m_callLinkInfos);
     }
 
-    Vector<WasmToJSStub>&& takeWasmToJSStubs()
+    Vector<WasmExitStubs>&& takeWasmExitStubs()
     {
         RELEASE_ASSERT(!failed());
-        return WTFMove(m_wasmToJSStubs);
-    }
-
-    ImmutableFunctionIndexSpace&& takeFunctionIndexSpace()
-    {
-        RELEASE_ASSERT(!failed());
-        return WTFMove(m_functionIndexSpace);
+        return WTFMove(m_wasmExitStubs);
     }
 
 private:
     std::unique_ptr<ModuleInformation> m_moduleInformation;
     Vector<FunctionLocationInBinary> m_functionLocationInBinary;
+    Vector<SignatureIndex> m_moduleSignatureIndicesToUniquedSignatureIndices;
     Bag<CallLinkInfo> m_callLinkInfos;
-    Vector<WasmToJSStub> m_wasmToJSStubs;
+    Vector<WasmExitStubs> m_wasmExitStubs;
     Vector<std::unique_ptr<WasmInternalFunction>> m_wasmInternalFunctions;
     Vector<CompilationContext> m_compilationContexts;
-    ImmutableFunctionIndexSpace m_functionIndexSpace;
 
     VM* m_vm;
     Vector<Vector<UnlinkedWasmToWasmCall>> m_unlinkedWasmToWasmCalls;
index 6d5b1f6..0966dab 100644 (file)
@@ -41,12 +41,22 @@ const bool verbose = false;
 
 const constexpr SignatureIndex Signature::invalidIndex;
 
+String Signature::toString() const
+{
+    String result(makeString(returnType()));
+    result.append(" (");
+    for (SignatureArgCount arg = 0; arg < argumentCount(); ++arg) {
+        if (arg)
+            result.append(", ");
+        result.append(makeString(argument(arg)));
+    }
+    result.append(')');
+    return result;
+}
+
 void Signature::dump(PrintStream& out) const
 {
-    out.print(makeString(returnType()), " (");
-    for (SignatureArgCount arg = 0; arg < argumentCount(); ++arg)
-        out.print((arg ? ", " : ""), makeString(argument(arg)));
-    out.print(")");
+    out.print(toString());
 }
 
 unsigned Signature::hash() const
index 0d6c7b5..4d43ffc 100644 (file)
@@ -82,6 +82,7 @@ public:
     }
     Type argument(SignatureArgCount i) const { return const_cast<Signature*>(this)->argument(i); }
 
+    WTF::String toString() const;
     void dump(WTF::PrintStream& out) const;
     bool operator==(const Signature& rhs) const
     {
index e321613..75d3826 100644 (file)
@@ -381,10 +381,10 @@ void Validate::dump(const Vector<ControlEntry>& controlStack, const ExpressionLi
     dataLogLn();
 }
 
-Expected<void, String> validateFunction(VM* vm, const uint8_t* source, size_t length, const Signature* signature, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& module)
+Expected<void, String> validateFunction(VM* vm, const uint8_t* source, size_t length, const Signature* signature, const ModuleInformation& module, const Vector<SignatureIndex>& moduleSignatureIndicesToUniquedSignatureIndices)
 {
     Validate context(module);
-    FunctionParser<Validate> validator(vm, context, source, length, signature, functionIndexSpace, module);
+    FunctionParser<Validate> validator(vm, context, source, length, signature, module, moduleSignatureIndicesToUniquedSignatureIndices);
     WASM_FAIL_IF_HELPER_FAILS(validator.parse());
     return { };
 }
index 14821f2..73e7d87 100644 (file)
@@ -36,7 +36,7 @@ class VM;
 
 namespace Wasm {
 
-Expected<void, String> validateFunction(VM*, const uint8_t*, size_t, const Signature*, const ImmutableFunctionIndexSpace&, const ModuleInformation&);
+Expected<void, String> validateFunction(VM*, const uint8_t*, size_t, const Signature*, const ModuleInformation&, const Vector<SignatureIndex>&);
 
 } } // namespace JSC::Wasm
 
index 928f423..99a2c90 100644 (file)
@@ -86,9 +86,11 @@ public:
         return offsetOfImportFunctions() + sizeof(WriteBarrier<JSCell>) * idx;
     }
 
-    static ptrdiff_t offsetOfTable() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_table); }
     static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_memory); }
+    static ptrdiff_t offsetOfTable() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_table); }
     static ptrdiff_t offsetOfGlobals() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_globals); }
+    static size_t offsetOfImportFunctions() { return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSCell>)>(sizeof(JSWebAssemblyInstance)); }
+    static size_t offsetOfImportFunction(size_t importFunctionNum) { return offsetOfImportFunctions() + importFunctionNum * sizeof(sizeof(WriteBarrier<JSCell>)); }
 
 protected:
     JSWebAssemblyInstance(VM&, Structure*, unsigned numImportFunctions);
@@ -96,11 +98,6 @@ protected:
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
 
-    static size_t offsetOfImportFunctions()
-    {
-        return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSCell>)>(sizeof(JSWebAssemblyInstance));
-    }
-
     static size_t allocationSize(unsigned numImportFunctions)
     {
         return offsetOfImportFunctions() + sizeof(WriteBarrier<JSCell>) * numImportFunctions;
index 528ab69..4c4cc3e 100644 (file)
@@ -37,9 +37,9 @@ namespace JSC {
 
 const ClassInfo JSWebAssemblyMemory::s_info = { "WebAssembly.Memory", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyMemory) };
 
-JSWebAssemblyMemory* JSWebAssemblyMemory::create(VM& vm, Structure* structure, std::unique_ptr<Wasm::Memory>&& memory)
+JSWebAssemblyMemory* JSWebAssemblyMemory::create(VM& vm, Structure* structure, Wasm::Memory&& memory)
 {
-    auto* instance = new (NotNull, allocateCell<JSWebAssemblyMemory>(vm.heap)) JSWebAssemblyMemory(vm, structure, WTFMove(memory));
+    auto* instance = new (NotNull, allocateCell<JSWebAssemblyMemory>(vm.heap)) JSWebAssemblyMemory(vm, structure, std::forward<Wasm::Memory>(memory));
     instance->finishCreation(vm);
     return instance;
 }
@@ -49,7 +49,7 @@ Structure* JSWebAssemblyMemory::createStructure(VM& vm, JSGlobalObject* globalOb
     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
 }
 
-JSWebAssemblyMemory::JSWebAssemblyMemory(VM& vm, Structure* structure, std::unique_ptr<Wasm::Memory>&& memory)
+JSWebAssemblyMemory::JSWebAssemblyMemory(VM& vm, Structure* structure, Wasm::Memory&& memory)
     : Base(vm, structure)
     , m_memory(WTFMove(memory))
 {
@@ -72,7 +72,7 @@ JSArrayBuffer* JSWebAssemblyMemory::buffer(VM& vm, JSGlobalObject* globalObject)
     auto destructor = [] (void*) {
         // We don't need to do anything here to destroy the memory.
         // The ArrayBuffer backing the JSArrayBuffer is only owned by us,
-        // so we guarantee its lifecylce.
+        // so we guarantee its lifecycle.
     };
     m_buffer = ArrayBuffer::createFromBytes(memory()->memory(), memory()->size(), WTFMove(destructor));
     m_bufferWrapper.set(vm, this, JSArrayBuffer::create(vm, globalObject->m_arrayBufferStructure.get(), m_buffer.get()));
@@ -85,7 +85,7 @@ Wasm::PageCount JSWebAssemblyMemory::grow(ExecState* exec, uint32_t delta, bool
     VM& vm = exec->vm();
     auto throwScope = DECLARE_THROW_SCOPE(vm);
 
-    Wasm::PageCount oldPageCount = m_memory->sizeInPages();
+    Wasm::PageCount oldPageCount = memory()->sizeInPages();
 
     if (!Wasm::PageCount::isValid(delta)) {
         if (shouldThrowExceptionsOnFailure)
@@ -101,7 +101,7 @@ Wasm::PageCount JSWebAssemblyMemory::grow(ExecState* exec, uint32_t delta, bool
     }
 
     if (delta) {
-        bool success = m_memory->grow(newSize);
+        bool success = memory()->grow(newSize);
         if (!success) {
             if (shouldThrowExceptionsOnFailure)
                 throwException(exec, throwScope, createOutOfMemoryError(exec));
index 28797c6..b422f48 100644 (file)
@@ -41,25 +41,26 @@ class JSWebAssemblyMemory : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-    static JSWebAssemblyMemory* create(VM&, Structure*, std::unique_ptr<Wasm::Memory>&&);
+    static JSWebAssemblyMemory* create(VM&, Structure*, Wasm::Memory&&);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
 
-    Wasm::Memory* memory() { return m_memory.get(); }
+    Wasm::Memory* memory() { return &m_memory; }
     JSArrayBuffer* buffer(VM& vm, JSGlobalObject*);
     Wasm::PageCount grow(ExecState*, uint32_t delta, bool shouldThrowExceptionsOnFailure);
 
-    static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(JSWebAssemblyMemory, m_memory); }
+    static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(JSWebAssemblyMemory, m_memory) + Wasm::Memory::offsetOfMemory(); }
+    static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(JSWebAssemblyMemory, m_memory) + Wasm::Memory::offsetOfSize(); }
 
 protected:
-    JSWebAssemblyMemory(VM&, Structure*, std::unique_ptr<Wasm::Memory>&&);
+    JSWebAssemblyMemory(VM&, Structure*, Wasm::Memory&&);
     ~JSWebAssemblyMemory();
     void finishCreation(VM&);
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
 
-    std::unique_ptr<Wasm::Memory> m_memory;
+    Wasm::Memory m_memory;
     WriteBarrier<JSArrayBuffer> m_bufferWrapper;
     RefPtr<ArrayBuffer> m_buffer;
 };
index d6524c5..fa0e859 100644 (file)
@@ -38,9 +38,9 @@ namespace JSC {
 
 const ClassInfo JSWebAssemblyModule::s_info = { "WebAssembly.Module", &Base::s_info, nullptr, CREATE_METHOD_TABLE(JSWebAssemblyModule) };
 
-JSWebAssemblyModule* JSWebAssemblyModule::create(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>&& moduleInformation, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmToJSStub>&& wasmToJSStubs, Wasm::ImmutableFunctionIndexSpace&& functionIndexSpace, SymbolTable* exportSymbolTable, unsigned calleeCount)
+JSWebAssemblyModule* JSWebAssemblyModule::create(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>&& moduleInformation, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmExitStubs>&& wasmExitStubs, SymbolTable* exportSymbolTable, unsigned calleeCount)
 {
-    auto* instance = new (NotNull, allocateCell<JSWebAssemblyModule>(vm.heap, allocationSize(calleeCount))) JSWebAssemblyModule(vm, structure, std::forward<std::unique_ptr<Wasm::ModuleInformation>>(moduleInformation), std::forward<Bag<CallLinkInfo>>(callLinkInfos), std::forward<Vector<Wasm::WasmToJSStub>>(wasmToJSStubs), std::forward<Wasm::ImmutableFunctionIndexSpace>(functionIndexSpace), calleeCount);
+    auto* instance = new (NotNull, allocateCell<JSWebAssemblyModule>(vm.heap, allocationSize(calleeCount))) JSWebAssemblyModule(vm, structure, std::forward<std::unique_ptr<Wasm::ModuleInformation>>(moduleInformation), std::forward<Bag<CallLinkInfo>>(callLinkInfos), std::forward<Vector<Wasm::WasmExitStubs>>(wasmExitStubs), calleeCount);
     instance->finishCreation(vm, exportSymbolTable);
     return instance;
 }
@@ -50,12 +50,11 @@ Structure* JSWebAssemblyModule::createStructure(VM& vm, JSGlobalObject* globalOb
     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
 }
 
-JSWebAssemblyModule::JSWebAssemblyModule(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>&& moduleInformation, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmToJSStub>&& wasmToJSStubs, Wasm::ImmutableFunctionIndexSpace&& functionIndexSpace, unsigned calleeCount)
+JSWebAssemblyModule::JSWebAssemblyModule(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>&& moduleInformation, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmExitStubs>&& wasmExitStubs, unsigned calleeCount)
     : Base(vm, structure)
     , m_moduleInformation(WTFMove(moduleInformation))
     , m_callLinkInfos(WTFMove(callLinkInfos))
-    , m_wasmToJSStubs(WTFMove(wasmToJSStubs))
-    , m_functionIndexSpace(WTFMove(functionIndexSpace))
+    , m_wasmExitStubs(WTFMove(wasmExitStubs))
     , m_calleeCount(calleeCount)
 {
     memset(callees(), 0, m_calleeCount * sizeof(WriteBarrier<JSWebAssemblyCallee>) * 2);
index fec4eed..7e051f5 100644 (file)
@@ -43,19 +43,18 @@ class JSWebAssemblyModule : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-    static JSWebAssemblyModule* create(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&&, Bag<CallLinkInfo>&&, Vector<Wasm::WasmToJSStub>&&, Wasm::ImmutableFunctionIndexSpace&&, SymbolTable*, unsigned);
+    static JSWebAssemblyModule* create(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&&, Bag<CallLinkInfo>&&, Vector<Wasm::WasmExitStubs>&&, SymbolTable*, unsigned);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
 
     const Wasm::ModuleInformation& moduleInformation() const { return *m_moduleInformation.get(); }
     SymbolTable* exportSymbolTable() const { return m_exportSymbolTable.get(); }
-    Wasm::SignatureIndex signatureForFunctionIndexSpace(unsigned functionIndexSpace) const
+    Wasm::SignatureIndex signatureIndexFromFunctionIndexSpace(unsigned functionIndexSpace) const
     {
-        ASSERT(functionIndexSpace < m_functionIndexSpace.size);
-        return m_functionIndexSpace.buffer.get()[functionIndexSpace].signatureIndex;
+        return m_moduleInformation->signatureIndexFromFunctionIndexSpace(functionIndexSpace);
     }
-    unsigned functionImportCount() const { return m_wasmToJSStubs.size(); }
+    unsigned functionImportCount() const { return m_wasmExitStubs.size(); }
 
     JSWebAssemblyCallee* jsEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)
     {
@@ -90,10 +89,8 @@ public:
         return bitwise_cast<WriteBarrier<JSWebAssemblyCallee>*>(bitwise_cast<char*>(this) + offsetOfCallees());
     }
 
-    static ptrdiff_t offsetOfFunctionIndexSpace() { return OBJECT_OFFSETOF(JSWebAssemblyModule, m_functionIndexSpace); }
-
 protected:
-    JSWebAssemblyModule(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&&, Bag<CallLinkInfo>&&, Vector<Wasm::WasmToJSStub>&&, Wasm::ImmutableFunctionIndexSpace&&, unsigned calleeCount);
+    JSWebAssemblyModule(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&&, Bag<CallLinkInfo>&&, Vector<Wasm::WasmExitStubs>&&, unsigned calleeCount);
     void finishCreation(VM&, SymbolTable*);
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
@@ -117,8 +114,7 @@ private:
     std::unique_ptr<Wasm::ModuleInformation> m_moduleInformation;
     Bag<CallLinkInfo> m_callLinkInfos;
     WriteBarrier<SymbolTable> m_exportSymbolTable;
-    Vector<Wasm::WasmToJSStub> m_wasmToJSStubs;
-    const Wasm::ImmutableFunctionIndexSpace m_functionIndexSpace;
+    Vector<Wasm::WasmExitStubs> m_wasmExitStubs;
     unsigned m_calleeCount;
 };
 
index be27f57..b810189 100644 (file)
@@ -119,6 +119,7 @@ static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
     ProtoCallFrame protoCallFrame;
     protoCallFrame.init(nullptr, wasmFunction, firstArgument, argCount, remainingArgs);
 
+    // FIXME Do away with this entire function, and only use the entrypoint generated by B3. https://bugs.webkit.org/show_bug.cgi?id=166486
     JSWebAssemblyInstance* prevJSWebAssemblyInstance = vm.topJSWebAssemblyInstance;
     vm.topJSWebAssemblyInstance = wasmFunction->instance();
     ASSERT(wasmFunction->instance());
@@ -149,7 +150,7 @@ WebAssemblyFunction* WebAssemblyFunction::create(VM& vm, JSGlobalObject* globalO
 {
     NativeExecutable* executable = vm.getHostFunction(callWebAssemblyFunction, NoIntrinsic, callHostFunctionAsConstructor, nullptr, name);
     Structure* structure = globalObject->webAssemblyFunctionStructure();
-    WebAssemblyFunction* function = new (NotNull, allocateCell<WebAssemblyFunction>(vm.heap)) WebAssemblyFunction(vm, globalObject, structure, signatureIndex);
+    WebAssemblyFunction* function = new (NotNull, allocateCell<WebAssemblyFunction>(vm.heap)) WebAssemblyFunction(vm, globalObject, structure, wasmEntrypoint, signatureIndex);
     function->finishCreation(vm, executable, length, name, instance, jsEntrypoint, wasmEntrypoint);
     return function;
 }
@@ -157,11 +158,12 @@ WebAssemblyFunction* WebAssemblyFunction::create(VM& vm, JSGlobalObject* globalO
 Structure* WebAssemblyFunction::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
 {
     ASSERT(globalObject);
-    return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info());
+    return Structure::create(vm, globalObject, prototype, TypeInfo(WebAssemblyFunctionType, StructureFlags), info());
 }
 
-WebAssemblyFunction::WebAssemblyFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, Wasm::SignatureIndex signatureIndex)
+WebAssemblyFunction::WebAssemblyFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSWebAssemblyCallee* wasmEntrypoint, Wasm::SignatureIndex signatureIndex)
     : Base(vm, globalObject, structure)
+    , m_wasmEntryPointCode(wasmEntrypoint->entrypoint())
     , m_signatureIndex(signatureIndex)
 { }
 
@@ -183,6 +185,7 @@ void WebAssemblyFunction::finishCreation(VM& vm, NativeExecutable* executable, u
     ASSERT(jsEntrypoint != wasmEntrypoint);
     m_jsEntrypoint.set(vm, this, jsEntrypoint);
     m_wasmEntrypoint.set(vm, this, wasmEntrypoint);
+    ASSERT(m_wasmEntrypoint->entrypoint() == m_wasmEntryPointCode);
 }
 
 } // namespace JSC
index 3490502..26694c1 100644 (file)
@@ -54,18 +54,22 @@ public:
 
     JSWebAssemblyInstance* instance() const { return m_instance.get(); }
     Wasm::SignatureIndex signatureIndex() const { return m_signatureIndex; }
-    void* wasmEntrypoint() { return m_wasmEntrypoint->entrypoint(); }
+    void* wasmEntrypoint() { return m_wasmEntryPointCode; }
     void* jsEntrypoint() { return m_jsEntrypoint->entrypoint(); }
 
+    static ptrdiff_t offsetOfInstance() { return OBJECT_OFFSETOF(WebAssemblyFunction, m_instance); }
+    static ptrdiff_t offsetOfWasmEntryPointCode() { return OBJECT_OFFSETOF(WebAssemblyFunction, m_wasmEntryPointCode); }
+
 protected:
     static void visitChildren(JSCell*, SlotVisitor&);
 
     void finishCreation(VM&, NativeExecutable*, unsigned length, const String& name, JSWebAssemblyInstance*, JSWebAssemblyCallee* jsEntrypoint, JSWebAssemblyCallee* wasmEntrypoint);
 
 private:
-    WebAssemblyFunction(VM&, JSGlobalObject*, Structure*, Wasm::SignatureIndex);
+    WebAssemblyFunction(VM&, JSGlobalObject*, Structure*, JSWebAssemblyCallee*, Wasm::SignatureIndex);
 
     WriteBarrier<JSWebAssemblyInstance> m_instance;
+    void* m_wasmEntryPointCode; // Cache code pointer: allows the wasm -> wasm stub to do a single load and jump instead of having dependent loads.
     WriteBarrier<JSWebAssemblyCallee> m_jsEntrypoint;
     WriteBarrier<JSWebAssemblyCallee> m_wasmEntrypoint;
     Wasm::SignatureIndex m_signatureIndex;
index d60f40e..1b46112 100644 (file)
@@ -84,6 +84,11 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
 
     JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, instanceStructure, jsModule, moduleRecord->getModuleNamespace(exec));
     RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
+    {
+        // Always start with a dummy Memory, so that wasm -> wasm thunks avoid checking for a nullptr Memory when trying to set pinned registers.
+        Wasm::Memory memory;
+        instance->setMemory(vm, JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory)));
+    }
 
     // Let funcs, memories and tables be initially-empty lists of callable JavaScript objects, WebAssembly.Memory objects and WebAssembly.Table objects, respectively.
     // Let imports be an initially-empty list of external values.
@@ -114,18 +119,19 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
                 return JSValue::encode(throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("import function must be callable"))));
             JSCell* cell = value.asCell();
             // ii. If v is an Exported Function Exotic Object:
-            if (WebAssemblyFunction* importedExports = jsDynamicCast<WebAssemblyFunction*>(object)) {
-                // FIXME handle Function Exotic Object properly. https://bugs.webkit.org/show_bug.cgi?id=165282
-                // a. If the signature of v does not match the signature of i, throw a TypeError.
+            if (WebAssemblyFunction* importedExport = jsDynamicCast<WebAssemblyFunction*>(cell)) {
+                // a. If the signature of v does not match the signature of i, throw a WebAssembly.LinkError.
+                Wasm::SignatureIndex importedSignatureIndex = importedExport->signatureIndex();
+                Wasm::SignatureIndex expectedSignatureIndex = moduleInformation.importFunctionSignatureIndices[import.kindIndex];
+                if (importedSignatureIndex != expectedSignatureIndex)
+                    return JSValue::encode(throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("imported function's signature doesn't match the provided WebAssembly function's signature"))));
                 // b. Let closure be v.[[Closure]].
-                RELEASE_ASSERT_NOT_REACHED();
-                UNUSED_PARAM(importedExports);
-                break;
             }
             // iii. Otherwise:
             // a. Let closure be a new host function of the given signature which calls v by coercing WebAssembly arguments to JavaScript arguments via ToJSValue and returns the result, if any, by coercing via ToWebAssemblyValue.
             // Note: done as part of Plan compilation.
             // iv. Append v to funcs.
+            // Note: adding the JSCell to the instance list fulfills closure requirements b. above (the WebAssembly.Instance wil be kept alive) and v. below (the JSFunction).
             instance->setImportFunction(vm, cell, numImportFunctions++);
             // v. Append closure to imports.
             break;
@@ -230,7 +236,7 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
             RELEASE_ASSERT(!moduleInformation.memory.isImport());
             // We create a memory when it's a memory definition.
             bool failed;
-            std::unique_ptr<Wasm::Memory> memory = std::make_unique<Wasm::Memory>(moduleInformation.memory.initial(), moduleInformation.memory.maximum(), failed);
+            Wasm::Memory memory(moduleInformation.memory.initial(), moduleInformation.memory.maximum(), failed);
             if (failed)
                 return JSValue::encode(throwException(exec, throwScope, createOutOfMemoryError(exec)));
             instance->setMemory(vm,
index 5d5a6cd..6c887af 100644 (file)
@@ -97,7 +97,7 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyMemory(ExecState* exec
     }
 
     bool failed;
-    std::unique_ptr<Wasm::Memory> memory = std::make_unique<Wasm::Memory>(initialPageCount, maximumPageCount, failed);
+    Wasm::Memory memory(initialPageCount, maximumPageCount, failed);
     if (failed)
         return JSValue::encode(throwException(exec, throwScope, createOutOfMemoryError(exec)));
 
index 38edb8c..8129592 100644 (file)
@@ -97,7 +97,7 @@ JSValue WebAssemblyModuleConstructor::createModule(ExecState* state, Structure*
 
     // Only wasm-internal functions have a callee, stubs to JS do not.
     unsigned calleeCount = plan.internalFunctionCount();
-    JSWebAssemblyModule* result = JSWebAssemblyModule::create(vm, structure, plan.takeModuleInformation(), plan.takeCallLinkInfos(), plan.takeWasmToJSStubs(), plan.takeFunctionIndexSpace(), exportSymbolTable, calleeCount);
+    JSWebAssemblyModule* result = JSWebAssemblyModule::create(vm, structure, plan.takeModuleInformation(), plan.takeCallLinkInfos(), plan.takeWasmExitStubs(), exportSymbolTable, calleeCount);
     plan.initializeCallees(state->jsCallee()->globalObject(), 
         [&] (unsigned calleeIndex, JSWebAssemblyCallee* jsEntrypointCallee, JSWebAssemblyCallee* wasmEntrypointCallee) {
             result->setJSEntrypointCallee(vm, calleeIndex, jsEntrypointCallee);
index c1bd820..9eff4c4 100644 (file)
@@ -118,7 +118,7 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
             //     c. Return func.
             JSWebAssemblyCallee* jsEntrypointCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
             JSWebAssemblyCallee* wasmEntrypointCallee = module->wasmEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
-            Wasm::SignatureIndex signatureIndex = module->signatureForFunctionIndexSpace(exp.kindIndex);
+            Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(exp.kindIndex);
             const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex);
             WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->argumentCount(), exp.field.string(), instance, jsEntrypointCallee, wasmEntrypointCallee, signatureIndex);
             exportedValue = function;
@@ -133,8 +133,6 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
             break;
         }
         case Wasm::ExternalKind::Memory: {
-            // This should be guaranteed by module verification.
-            RELEASE_ASSERT(instance->memory()); 
             ASSERT(exp.kindIndex == 0);
 
             exportedValue = instance->memory();
@@ -175,7 +173,7 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
     bool hasStart = !!moduleInformation.startFunctionIndexSpace;
     if (hasStart) {
         auto startFunctionIndexSpace = moduleInformation.startFunctionIndexSpace.value_or(0);
-        Wasm::SignatureIndex signatureIndex = module->signatureForFunctionIndexSpace(startFunctionIndexSpace);
+        Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(startFunctionIndexSpace);
         const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex);
         // The start function must not take any arguments or return anything. This is enforced by the parser.
         ASSERT(!signature->argumentCount());
@@ -238,7 +236,7 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* state)
 
                 JSWebAssemblyCallee* jsEntrypointCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(functionIndex);
                 JSWebAssemblyCallee* wasmEntrypointCallee = module->wasmEntrypointCalleeFromFunctionIndexSpace(functionIndex);
-                Wasm::SignatureIndex signatureIndex = module->signatureForFunctionIndexSpace(functionIndex);
+                Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(functionIndex);
                 const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex);
                 // FIXME: Say we export local function "foo" at funciton index 0.
                 // What if we also set it to the table an Element w/ index 0.
@@ -257,7 +255,6 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* state)
         const Vector<Wasm::Segment::Ptr>& data = m_instance->module()->moduleInformation().data;
         JSWebAssemblyMemory* jsMemory = m_instance->memory();
         if (!data.isEmpty()) {
-            RELEASE_ASSERT(jsMemory); // It is a validation error for a Data section to exist without a Memory section or import.
             uint8_t* memory = reinterpret_cast<uint8_t*>(jsMemory->memory()->memory());
             uint64_t sizeInBytes = jsMemory->memory()->size();
             for (auto& segment : data) {