+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
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}`);
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") {
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);
};
};
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);
};
};
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);
};
};
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);
};
};
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);
}
};
return (field, index) => {
assert.isNumber(index, `Memory exports only support number indices`);
section.data.push({field, kind: "Memory", index});
- return nextBuilder;
+ return _errorHandlingProxyFor(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);
}
};
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);
};
};
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;
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) => {
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;
importBuilder.Function = _importFunctionContinuation(this, s, importBuilder);
importBuilder.Memory = _importMemoryContinuation(this, s, importBuilder);
importBuilder.Table = _importTableContinuation(this, s, importBuilder);
- return importBuilder;
+ return _errorHandlingProxyFor(importBuilder);
};
break;
End: () => this
// FIXME: add ability to add this with whatever.
};
- return functionBuilder;
+ return _errorHandlingProxyFor(functionBuilder);
};
break;
End: () => this,
Table: ({initial, maximum, element}) => {
s.data.push({tableDescription: {initial, maximum, element}});
- return tableBuilder;
+ return _errorHandlingProxyFor(tableBuilder);
}
};
- return tableBuilder;
+ return _errorHandlingProxyFor(tableBuilder);
};
break;
End: () => this,
InitialMaxPages: (initial, max) => {
s.data.push({ initial, max });
- return memoryBuilder;
+ return _errorHandlingProxyFor(memoryBuilder);
}
};
- return memoryBuilder;
+ return _errorHandlingProxyFor(memoryBuilder);
};
break;
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;
exportBuilder.Function = _exportFunctionContinuation(this, s, exportBuilder);
exportBuilder.Memory = _exportMemoryContinuation(this, s, exportBuilder);
exportBuilder.Table = _exportTableContinuation(this, s, exportBuilder);
- return exportBuilder;
+ return _errorHandlingProxyFor(exportBuilder);
};
break;
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;
End: () => this,
Element: ({tableIndex = 0, offset, functionIndices}) => {
s.data.push({tableIndex, offset, functionIndices});
- return elementBuilder;
+ return _errorHandlingProxyFor(elementBuilder);
}
};
- return elementBuilder;
+ return _errorHandlingProxyFor(elementBuilder);
};
break;
// 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;
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;
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) {
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]`);
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);
}
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);
}
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");
}
--- /dev/null
+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)')`);
+ }
+ }
+})();
--- /dev/null
+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
wasm/WasmMemory.cpp
wasm/WasmMemoryInformation.cpp
wasm/WasmModuleParser.cpp
+ wasm/WasmPageCount.cpp
wasm/WasmPlan.cpp
wasm/WasmSignature.cpp
wasm/WasmValidate.cpp
+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
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 */,
lastIndex = calleeIndex;
});
}
-
std::unique_ptr<Wasm::ModuleInformation> moduleInformation = plan.takeModuleInformation();
RELEASE_ASSERT(!moduleInformation->memory);
JSMapType,
JSSetType,
+ WebAssemblyFunctionType,
+
LastJSCObjectType = JSSetType,
MaxJSType = 0b11111111,
};
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);
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);
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;
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)
}
}
- 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);
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);
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 {
JSWebAssemblyInstance* instance = vm.topJSWebAssemblyInstance;
JSWebAssemblyMemory* wasmMemory = instance->memory();
- RELEASE_ASSERT(wasmMemory); // This would fail validation otherwise.
if (delta < 0)
return -1;
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);
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 { };
}
});
});
- 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 { };
}
});
}
- // 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;
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>();
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();
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
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.
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))));
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
namespace Wasm {
-WasmToJSStub importStubGenerator(VM*, Bag<CallLinkInfo>&, SignatureIndex, unsigned);
+WasmExitStubs exitStubGenerator(VM*, Bag<CallLinkInfo>&, SignatureIndex, unsigned);
} } // namespace JSC::Wasm
};
struct ModuleInformation {
- Vector<SignatureIndex> signatureIndices;
Vector<Import> imports;
Vector<SignatureIndex> importFunctionSignatureIndices;
Vector<SignatureIndex> internalFunctionSignatureIndices;
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; }
struct UnlinkedWasmToWasmCall {
CodeLocationCall callLocation;
size_t functionIndex;
+ enum class Target : uint8_t {
+ ToJs,
+ ToWasm,
+ } target;
};
struct Entrypoint {
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.
};
typedef Vector<CallableFunction> FunctionIndexSpace;
-
-struct ImmutableFunctionIndexSpace {
- MallocPtr<CallableFunction> buffer;
- size_t size;
-};
-
} } // namespace JSC::Wasm
#endif // ENABLE(WEBASSEMBLY)
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();
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);
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");
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");
#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
{
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;
}
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;
}
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;
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;
}
m_mappedCapacity = desiredSize;
m_size = desiredSize;
+ if (verbose)
+ dataLogLn("Memory::grow ", *this);
return true;
}
#include "WasmCallingConvention.h"
#include "WasmPageCount.h"
-#include <wtf/Vector.h>
+namespace WTF {
+class PrintStream;
+}
namespace JSC { namespace Wasm {
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
#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)
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
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 {
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; }
private:
PageCount m_initial { };
PageCount m_maximum { };
- PinnedRegisterInfo m_pinnedRegisters { };
bool m_isImport { false };
};
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;
signature->returnType() = returnType;
SignatureIndex signatureIndex = SignatureInformation::adopt(m_vm, signature.release());
- m_result.module->signatureIndices.uncheckedAppend(signatureIndex);
+ m_result.moduleSignatureIndicesToUniquedSignatureIndices.uncheckedAppend(signatureIndex);
}
return { };
}
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;
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: {
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 { };
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 { };
}
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: {
{
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");
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);
}
struct ModuleParserResult {
std::unique_ptr<ModuleInformation> module;
- FunctionIndexSpace functionIndexSpace;
Vector<FunctionLocationInBinary> functionLocationInBinary;
+ Vector<SignatureIndex> moduleSignatureIndicesToUniquedSignatureIndices;
};
class ModuleParser : public Parser<ModuleParserResult> {
--- /dev/null
+/*
+ * 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)
#if ENABLE(WEBASSEMBLY)
+namespace WTF {
+class PrintStream;
+}
+
namespace JSC { namespace Wasm {
class PageCount {
: 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; }
}
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) {
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)
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"))
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;
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);
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));
}
{
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()) {
// 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;
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;
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
}
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
{
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 { };
}
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
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);
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;
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;
}
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))
{
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()));
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)
}
if (delta) {
- bool success = m_memory->grow(newSize);
+ bool success = memory()->grow(newSize);
if (!success) {
if (shouldThrowExceptionsOnFailure)
throwException(exec, throwScope, createOutOfMemoryError(exec));
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;
};
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;
}
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);
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)
{
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&);
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;
};
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());
{
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;
}
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)
{ }
ASSERT(jsEntrypoint != wasmEntrypoint);
m_jsEntrypoint.set(vm, this, jsEntrypoint);
m_wasmEntrypoint.set(vm, this, wasmEntrypoint);
+ ASSERT(m_wasmEntrypoint->entrypoint() == m_wasmEntryPointCode);
}
} // namespace JSC
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;
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.
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;
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,
}
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)));
// 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);
// 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;
break;
}
case Wasm::ExternalKind::Memory: {
- // This should be guaranteed by module verification.
- RELEASE_ASSERT(instance->memory());
ASSERT(exp.kindIndex == 0);
exportedValue = instance->memory();
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());
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.
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) {