Diff
Modified: trunk/JSTests/ChangeLog (209829 => 209830)
--- trunk/JSTests/ChangeLog 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/JSTests/ChangeLog 2016-12-14 21:29:14 UTC (rev 209830)
@@ -1,3 +1,34 @@
+2016-12-14 Keith Miller <[email protected]>
+
+ WebAssembly JS API: implement Global
+ https://bugs.webkit.org/show_bug.cgi?id=164133
+
+ Reviewed by Saam Barati.
+
+ * wasm/Builder.js:
+ (export.default.Builder.prototype._registerSectionBuilders.switch.case.string_appeared_here.this.section):
+ * wasm/Builder_WebAssemblyBinary.js:
+ (const.valueType.WASM.description.type.i32.type.const.putGlobalType):
+ (const.putOp):
+ (const.putInitExpr):
+ (const.emitters.Import):
+ (const.emitters.Global):
+ (const.emitters.Export):
+ (const.emitters.Code):
+ * wasm/LowLevelBinary.js:
+ (export.default.LowLevelBinary.prototype.varuint32):
+ (export.default.LowLevelBinary.prototype.varint32):
+ * wasm/js-api/global-error.js: Added.
+ (catch):
+ (assert.truthy):
+ * wasm/js-api/global-external-init-from-import.js: Added.
+ * wasm/js-api/global-internal-init-from-import.js: Added.
+ * wasm/js-api/global-mutate.js: Added.
+ (createInternalGlobalModule):
+ * wasm/js-api/globals-export.js: Added.
+ * wasm/js-api/globals-import.js: Added.
+ * wasm/wasm.json:
+
2016-12-13 Saam Barati <[email protected]>
WebAssembly: implement the elements section
Modified: trunk/JSTests/wasm/Builder.js (209829 => 209830)
--- trunk/JSTests/wasm/Builder.js 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/JSTests/wasm/Builder.js 2016-12-14 21:29:14 UTC (rev 209830)
@@ -174,6 +174,41 @@
};
};
+const _normalizeMutability = (mutability) => {
+ if (mutability === "mutable")
+ return 1;
+ else if (mutability === "immutable")
+ return 0;
+ else
+ throw new Error(`mutability should be either "mutable" or "immutable", but got ${global.mutablity}`);
+};
+
+const _exportGlobalContinuation = (builder, section, nextBuilder) => {
+ return (field, index) => {
+ assert.isNumber(index, `Global exports only support number indices right now`);
+ section.data.push({ field, kind: "Global", index });
+ return nextBuilder;
+ }
+};
+
+const _importGlobalContinuation = (builder, section, nextBuilder) => {
+ return () => {
+ const globalBuilder = {
+ End: () => nextBuilder
+ };
+ for (let op of WASM.description.value_type) {
+ globalBuilder[_toJavaScriptName(op)] = (module, field, mutability) => {
+ assert.isString(module, `Import global module should be a string, got "${module}"`);
+ 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 globalBuilder;
+ };
+};
+
const _checkStackArgs = (op, param) => {
for (let expect of param) {
if (WASM.isValidType(expect)) {
@@ -202,6 +237,7 @@
} else {
// Handle our own meta-types.
switch (expect) {
+ case "any": break;
case "bool": break; // FIXME implement bool. https://bugs.webkit.org/show_bug.cgi?id=163421
case "call": break; // FIXME implement call stack return check based on function signature. https://bugs.webkit.org/show_bug.cgi?id=163421
case "control": break; // FIXME implement control. https://bugs.webkit.org/show_bug.cgi?id=163421
@@ -407,8 +443,8 @@
const s = this._addSection(section);
const importBuilder = {
End: () => this,
- Global: () => { throw new Error(`Unimplemented: import global`); },
};
+ importBuilder.Global = _importGlobalContinuation(this, s, importBuilder);
importBuilder.Function = _importFunctionContinuation(this, s, importBuilder);
importBuilder.Memory = _importMemoryContinuation(this, s, importBuilder);
importBuilder.Table = _importTableContinuation(this, s, importBuilder);
@@ -456,8 +492,23 @@
break;
case "Global":
- // FIXME implement global https://bugs.webkit.org/show_bug.cgi?id=164133
- this[section] = () => { throw new Error(`Unimplemented: section type "${section}"`); };
+ this[section] = function() {
+ const s = this._addSection(section);
+ const globalBuilder = {
+ End: () => this,
+ GetGlobal: (type, initValue, mutability) => {
+ s.data.push({ type, op: "get_global", mutability: _normalizeMutability(mutability), initValue });
+ return 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 globalBuilder;
+ };
break;
case "Export":
@@ -467,8 +518,8 @@
End: () => this,
Table: () => { throw new Error(`Unimplemented: export table`); },
Memory: () => { throw new Error(`Unimplemented: export memory`); },
- Global: () => { throw new Error(`Unimplemented: export global`); },
};
+ exportBuilder.Global = _exportGlobalContinuation(this, s, exportBuilder);
exportBuilder.Function = _exportFunctionContinuation(this, s, exportBuilder);
return exportBuilder;
};
@@ -508,7 +559,7 @@
const builder = this;
const codeBuilder = {
End: () => {
- // We now have enough information to remap the export section's "type" and "index" according to the Code section we're currently ending.
+ // We now have enough information to remap the export section's "type" and "index" according to the Code section we are currently ending.
const typeSection = builder._getSection("Type");
const importSection = builder._getSection("Import");
const exportSection = builder._getSection("Export");
@@ -516,6 +567,8 @@
const codeSection = s;
if (exportSection) {
for (const e of exportSection.data) {
+ if (e.kind !== "Function" || typeof(e.type) !== "undefined")
+ continue;
switch (typeof(e.index)) {
default: throw new Error(`Unexpected export index "${e.index}"`);
case "string": {
Modified: trunk/JSTests/wasm/Builder_WebAssemblyBinary.js (209829 => 209830)
--- trunk/JSTests/wasm/Builder_WebAssemblyBinary.js 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/JSTests/wasm/Builder_WebAssemblyBinary.js 2016-12-14 21:29:14 UTC (rev 209830)
@@ -51,6 +51,40 @@
putResizableLimits(bin, initial, maximum);
};
+const valueType = WASM.description.type.i32.type
+
+const putGlobalType = (bin, global) => {
+ put(bin, valueType, WASM.typeValue[global.type]);
+ put(bin, "varuint1", global.mutability);
+};
+
+const putOp = (bin, op) => {
+ put(bin, "uint8", op.value);
+ if (op.arguments.length !== 0)
+ throw new Error(`Unimplemented: arguments`); // FIXME https://bugs.webkit.org/show_bug.cgi?id=162706
+
+ switch (op.name) {
+ default:
+ for (let i = 0; i < op.immediates.length; ++i) {
+ const type = WASM.description.opcode[op.name].immediate[i].type
+ if (!bin[type])
+ throw new TypeError(`Unknown type: ${type} in op: ${op.name}`);
+ put(bin, type, op.immediates[i]);
+ }
+ break;
+ case "br_table":
+ put(bin, "varuint32", op.immediates.length - 1);
+ for (let imm of op.immediates)
+ put(bin, "varuint32", imm);
+ break;
+ }
+};
+
+const putInitExpr = (bin, expr) => {
+ putOp(bin, { value: WASM.description.opcode[expr.op].value, name: expr.op, immediates: [expr.initValue], arguments: [] });
+ putOp(bin, { value: WASM.description.opcode.end.value, name: "end", immediates: [], arguments: [] });
+};
+
const emitters = {
Type: (section, bin) => {
put(bin, "varuint32", section.data.length);
@@ -88,7 +122,9 @@
putResizableLimits(bin, initial, maximum);
break;
};
- case "Global": throw new Error(`Not yet implemented`);
+ case "Global":
+ putGlobalType(bin, entry.globalDescription);
+ break;
}
}
},
@@ -117,7 +153,14 @@
}
},
- Global: (section, bin) => { throw new Error(`Not yet implemented`); },
+ Global: (section, bin) => {
+ put(bin, "varuint32", section.data.length);
+ for (const global of section.data) {
+ putGlobalType(bin, global);
+ putInitExpr(bin, global)
+ }
+ },
+
Export: (section, bin) => {
put(bin, "varuint32", section.data.length);
for (const entry of section.data) {
@@ -125,10 +168,13 @@
put(bin, "uint8", WASM.externalKindValue[entry.kind]);
switch (entry.kind) {
default: throw new Error(`Implementation problem: unexpected kind ${entry.kind}`);
- case "Function": put(bin, "varuint32", entry.index); break;
+ case "Global":
+ case "Function":
+ put(bin, "varuint32", entry.index);
+ break;
case "Table": throw new Error(`Not yet implemented`);
case "Memory": throw new Error(`Not yet implemented`);
- case "Global": throw new Error(`Not yet implemented`);
+
}
}
},
@@ -164,27 +210,9 @@
put(funcBin, "varint7", WASM.typeValue[func.locals[i]]);
}
- for (const op of func.code) {
- put(funcBin, "uint8", op.value);
- if (op.arguments.length !== 0)
- throw new Error(`Unimplemented: arguments`); // FIXME https://bugs.webkit.org/show_bug.cgi?id=162706
+ for (const op of func.code)
+ putOp(funcBin, op);
- switch (op.name) {
- default:
- for (let i = 0; i < op.immediates.length; ++i) {
- const type = WASM.description.opcode[op.name].immediate[i].type
- if (!funcBin[type])
- throw new TypeError(`Unknown type: ${type} in op: ${op.name}`);
- put(funcBin, type, op.immediates[i]);
- }
- break;
- case "br_table":
- put(funcBin, "varuint32", op.immediates.length - 1);
- for (let imm of op.immediates)
- put(funcBin, "varuint32", imm);
- break;
- }
- }
funcBin.apply();
}
},
Modified: trunk/JSTests/wasm/LowLevelBinary.js (209829 => 209830)
--- trunk/JSTests/wasm/LowLevelBinary.js 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/JSTests/wasm/LowLevelBinary.js 2016-12-14 21:29:14 UTC (rev 209830)
@@ -123,6 +123,7 @@
this._push8(v >>> 24);
}
varuint32(v) {
+ assert.isNumber(v);
if (v < varuint32Min || varuint32Max < v)
throw new RangeError(`Invalid varuint32 ${v} range is [${varuint32Min}, ${varuint32Max}]`);
while (v >= 0x80) {
@@ -132,6 +133,7 @@
this.uint8(v);
}
varint32(v) {
+ assert.isNumber(v);
if (v < varint32Min || varint32Max < v)
throw new RangeError(`Invalid varint32 ${v} range is [${varint32Min}, ${varint32Max}]`);
do {
Added: trunk/JSTests/wasm/js-api/global-error.js (0 => 209830)
--- trunk/JSTests/wasm/js-api/global-error.js (rev 0)
+++ trunk/JSTests/wasm/js-api/global-error.js 2016-12-14 21:29:14 UTC (rev 209830)
@@ -0,0 +1,238 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+{
+ // Test init from non-import.
+ const builder = new Builder();
+
+ builder.Type().End()
+ .Function().End()
+ .Global().GetGlobal("i32", 0, "immutable").End()
+ .Export()
+ .Function("getGlobal")
+ .Global("global", 1)
+ .End()
+ .Code()
+
+ .Function("getGlobal", { params: [], ret: "i32" })
+ .GetGlobal(1)
+ .End()
+
+ .End()
+
+ const bin = builder.WebAssembly();
+ bin.trim();
+
+ let passed = false;
+ try {
+ const module = new WebAssembly.Module(bin.get());
+ } catch (e) {
+ if (e.message === "couldn't parse section Global: Global declarations (evaluating 'new WebAssembly.Module(bin.get())')")
+ passed = true;
+ }
+ assert.truthy(passed);
+}
+
+
+{
+ // Test import mutable.
+ const builder = new Builder();
+
+ builder.Type().End()
+ .Import()
+ .Global().I32("imp", "global", "mutable").End()
+ .End()
+ .Function().End()
+ .Global().GetGlobal("i32", 0, "immutable").End()
+ .Export()
+ .Function("getGlobal")
+ .Global("global", 1)
+ .End()
+ .Code()
+
+ .Function("getGlobal", { params: [], ret: "i32" })
+ .GetGlobal(1)
+ .End()
+
+ .End()
+
+ const bin = builder.WebAssembly();
+ bin.trim();
+
+ let passed = false;
+ try {
+ const module = new WebAssembly.Module(bin.get());
+ } catch (e) {
+ if (e.message === "couldn't parse section Import: Import declarations (evaluating 'new WebAssembly.Module(bin.get())')")
+ passed = true;
+ }
+ assert.truthy(passed);
+}
+
+{
+ // Test export mutable.
+ const builder = new Builder();
+
+ builder.Type().End()
+ .Function().End()
+ .Global().I32(0, "mutable").End()
+ .Export()
+ .Function("setGlobal")
+ .Global("global", 0)
+ .End()
+ .Code()
+
+ .Function("setGlobal", { params: [], ret: "i32" })
+ .GetGlobal(1)
+ .End()
+
+ .End()
+
+ const bin = builder.WebAssembly();
+ bin.trim();
+
+ let passed = false;
+ try {
+ const module = new WebAssembly.Module(bin.get());
+ } catch (e) {
+ if (e.message === "couldn't parse section Export: Exports (evaluating 'new WebAssembly.Module(bin.get())')")
+ passed = true;
+ }
+ assert.truthy(passed);
+}
+
+{
+ // Test set immutable.
+ const builder = new Builder();
+
+ builder.Type().End()
+ .Function().End()
+ .Global().I32(0, "immutable").End()
+ .Export()
+ .Function("setGlobal")
+ .Global("global", 0)
+ .End()
+ .Code()
+
+ .Function("setGlobal", { params: [], ret: "i32" })
+ .I32Const(0)
+ .SetGlobal(0)
+ .End()
+
+ .End()
+
+ const bin = builder.WebAssembly();
+ bin.trim();
+
+ let passed = false;
+ try {
+ const module = new WebAssembly.Module(bin.get());
+ } catch (e) {
+ if (e.message === "Attempt to store to immutable global. (evaluating 'new WebAssembly.Module(bin.get())')")
+ passed = true;
+ }
+ assert.truthy(passed);
+}
+
+
+{
+ // Test set non-existant global.
+ const builder = new Builder();
+
+ builder.Type().End()
+ .Function().End()
+ .Global().I32(0, "immutable").End()
+ .Export()
+ .Function("setGlobal")
+ .Global("global", 0)
+ .End()
+ .Code()
+
+ .Function("setGlobal", { params: [], ret: "i32" })
+ .I32Const(0)
+ .SetGlobal(1)
+ .End()
+
+ .End()
+
+ const bin = builder.WebAssembly();
+ bin.trim();
+
+ let passed = false;
+ try {
+ const module = new WebAssembly.Module(bin.get());
+ } catch (e) {
+ if (e.message === "Attempt to use unknown global. (evaluating 'new WebAssembly.Module(bin.get())')")
+ passed = true;
+ }
+ assert.truthy(passed);
+}
+
+
+{
+ // Test set to incorrect type
+ const builder = new Builder();
+
+ builder.Type().End()
+ .Function().End()
+ .Global().F32(0, "mutable").End()
+ .Export()
+ .Function("setGlobal")
+ .End()
+ .Code()
+
+ .Function("setGlobal", { params: [], ret: "i32" })
+ .I32Const(0)
+ .SetGlobal(0)
+ .End()
+
+ .End()
+
+ const bin = builder.WebAssembly();
+ bin.trim();
+
+ let passed = false;
+ try {
+ const module = new WebAssembly.Module(bin.get());
+ } catch (e) {
+ if (e.message === "Attempt to set global with type: F32 with a variable of type: I32 (evaluating 'new WebAssembly.Module(bin.get())')")
+ passed = true;
+ }
+ assert.truthy(passed);
+}
+
+for ( let imp of [undefined, null, {}, () => {}, "number", new Number(4)]) {
+ // Test import non-number.
+ const builder = new Builder();
+
+ builder.Type().End()
+ .Import()
+ .Global().I32("imp", "global", "immutable").End()
+ .End()
+ .Function().End()
+ .Global().GetGlobal("i32", 0, "immutable").End()
+ .Export()
+ .Function("getGlobal")
+ .Global("global", 1)
+ .End()
+ .Code()
+
+ .Function("getGlobal", { params: [], ret: "i32" })
+ .GetGlobal(1)
+ .End()
+
+ .End()
+
+ const bin = builder.WebAssembly();
+ bin.trim();
+
+ let passed = false;
+ const module = new WebAssembly.Module(bin.get());
+ try {
+ new WebAssembly.Instance(module, { imp: { global: imp } });
+ } catch (e) {
+ if (e.message === "imported global must be a number (evaluating 'new WebAssembly.Instance(module, { imp: { global: imp } })')")
+ passed = true;
+ }
+ assert.truthy(passed);
+}
Added: trunk/JSTests/wasm/js-api/global-external-init-from-import.js (0 => 209830)
--- trunk/JSTests/wasm/js-api/global-external-init-from-import.js (rev 0)
+++ trunk/JSTests/wasm/js-api/global-external-init-from-import.js 2016-12-14 21:29:14 UTC (rev 209830)
@@ -0,0 +1,30 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const builder = new Builder();
+
+builder.Type().End()
+ .Import()
+ .Global().I32("imp", "global", "immutable").End()
+ .End()
+ .Function().End()
+ .Global().GetGlobal("i32", 0, "immutable").End()
+ .Export()
+ .Function("getGlobal")
+ .Global("global", 1)
+ .End()
+ .Code()
+
+ .Function("getGlobal", { params: [], ret: "i32" })
+ .GetGlobal(1)
+ .End()
+
+ .End()
+
+const bin = builder.WebAssembly();
+bin.trim();
+
+const module = new WebAssembly.Module(bin.get());
+const instance = new WebAssembly.Instance(module, { imp: { global: 5 } });
+assert.eq(instance.exports.getGlobal(), 5);
+assert.eq(instance.exports.global, 5);
Added: trunk/JSTests/wasm/js-api/global-internal-init-from-import.js (0 => 209830)
--- trunk/JSTests/wasm/js-api/global-internal-init-from-import.js (rev 0)
+++ trunk/JSTests/wasm/js-api/global-internal-init-from-import.js 2016-12-14 21:29:14 UTC (rev 209830)
@@ -0,0 +1,26 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const builder = new Builder();
+
+builder.Type().End()
+ .Import()
+ .Global().I32("imp", "global", "immutable").End()
+ .End()
+ .Function().End()
+ .Global().GetGlobal("i32", 0, "mutable").End()
+ .Export().Function("getGlobal").End()
+ .Code()
+
+ .Function("getGlobal", { params: [], ret: "i32" })
+ .GetGlobal(1)
+ .End()
+
+ .End()
+
+const bin = builder.WebAssembly();
+bin.trim();
+
+const module = new WebAssembly.Module(bin.get());
+const instance = new WebAssembly.Instance(module, { imp: { global: 5 } });
+assert.eq(instance.exports.getGlobal(), 5);
Added: trunk/JSTests/wasm/js-api/global-mutate.js (0 => 209830)
--- trunk/JSTests/wasm/js-api/global-mutate.js (rev 0)
+++ trunk/JSTests/wasm/js-api/global-mutate.js 2016-12-14 21:29:14 UTC (rev 209830)
@@ -0,0 +1,39 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+function createInternalGlobalModule() {
+ const builder = new Builder();
+
+ builder.Type().End()
+ .Function().End()
+ .Global().I32(5, "mutable").End()
+ .Export()
+ .Function("getGlobal")
+ .Function("setGlobal")
+ .End()
+ .Code()
+
+ // GetGlobal
+ .Function("getGlobal", { params: [], ret: "i32" })
+ .GetGlobal(0)
+ .End()
+
+ // SetGlobal
+ .Function("setGlobal", { params: ["i32"] })
+ .GetLocal(0)
+ .SetGlobal(0)
+ .End()
+
+ .End()
+
+ const bin = builder.WebAssembly();
+ bin.trim();
+
+ const module = new WebAssembly.Module(bin.get());
+ const instance = new WebAssembly.Instance(module);
+ assert.eq(instance.exports.getGlobal(), 5);
+ instance.exports.setGlobal(3);
+ assert.eq(instance.exports.getGlobal(), 3);
+}
+
+createInternalGlobalModule();
Added: trunk/JSTests/wasm/js-api/globals-export.js (0 => 209830)
--- trunk/JSTests/wasm/js-api/globals-export.js (rev 0)
+++ trunk/JSTests/wasm/js-api/globals-export.js 2016-12-14 21:29:14 UTC (rev 209830)
@@ -0,0 +1,28 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const builder = new Builder();
+
+builder.Type().End()
+ .Import()
+ .Global().I32("imp", "global", "immutable").End()
+ .End()
+ .Function().End()
+ .Export()
+ .Function("getGlobal")
+ .Global("global", 0)
+ .End()
+ .Code()
+
+ .Function("getGlobal", { params: [], ret: "i32" })
+ .GetGlobal(0)
+ .End()
+
+ .End()
+
+const bin = builder.WebAssembly();
+bin.trim();
+
+const module = new WebAssembly.Module(bin.get());
+const instance = new WebAssembly.Instance(module, { imp: { global: 5 } });
+assert.eq(instance.exports.global, 5);
Added: trunk/JSTests/wasm/js-api/globals-import.js (0 => 209830)
--- trunk/JSTests/wasm/js-api/globals-import.js (rev 0)
+++ trunk/JSTests/wasm/js-api/globals-import.js 2016-12-14 21:29:14 UTC (rev 209830)
@@ -0,0 +1,29 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const builder = new Builder();
+
+builder.Type().End()
+ .Import()
+ .Global().I32("imp", "global", "immutable").End()
+ .End()
+ .Function().End()
+ .Global().I32(5, "mutable").End()
+ .Export()
+ .Function("getGlobal")
+ .End()
+ .Code()
+
+ // GetGlobal
+ .Function("getGlobal", { params: [], ret: "i32" })
+ .GetGlobal(0)
+ .End()
+
+ .End()
+
+const bin = builder.WebAssembly();
+bin.trim();
+
+const module = new WebAssembly.Module(bin.get());
+const instance = new WebAssembly.Instance(module, { imp: { global: 5 } });
+assert.eq(instance.exports.getGlobal(), 5);
Modified: trunk/JSTests/wasm/wasm.json (209829 => 209830)
--- trunk/JSTests/wasm/wasm.json 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/JSTests/wasm/wasm.json 2016-12-14 21:29:14 UTC (rev 209830)
@@ -58,11 +58,11 @@
"i64.const": { "category": "special", "value": 66, "return": ["i64"], "parameter": [], "immediate": [{"name": "value", "type": "varint64"}], "description": "a constant value interpreted as i64" },
"f64.const": { "category": "special", "value": 68, "return": ["f64"], "parameter": [], "immediate": [{"name": "value", "type": "uint64"}], "description": "a constant value interpreted as f64" },
"f32.const": { "category": "special", "value": 67, "return": ["f32"], "parameter": [], "immediate": [{"name": "value", "type": "uint32"}], "description": "a constant value interpreted as f32" },
- "get_local": { "category": "special", "value": 32, "return": ["local"], "parameter": [], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "read a local variable or parameter" },
- "set_local": { "category": "special", "value": 33, "return": [], "parameter": ["local"], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "write a local variable or parameter" },
- "tee_local": { "category": "special", "value": 34, "return": ["prev"], "parameter": ["any"], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "write a local variable or parameter and return the same value" },
- "get_global": { "category": "special", "value": 35, "return": ["global"], "parameter": [], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "read a global variable" },
- "set_global": { "category": "special", "value": 36, "return": [""], "parameter": ["global"], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "write a global variable" },
+ "get_local": { "category": "special", "value": 32, "return": ["any"], "parameter": [], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "read a local variable or parameter" },
+ "set_local": { "category": "special", "value": 33, "return": [], "parameter": ["any"], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "write a local variable or parameter" },
+ "tee_local": { "category": "special", "value": 34, "return": ["any"], "parameter": ["any"], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "write a local variable or parameter and return the same value" },
+ "get_global": { "category": "special", "value": 35, "return": ["any"], "parameter": [], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "read a global variable" },
+ "set_global": { "category": "special", "value": 36, "return": [], "parameter": ["any"], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "write a global variable" },
"call": { "category": "call", "value": 16, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "function_index", "type": "varuint32"}], "description": "call a function by its index" },
"call_indirect": { "category": "call", "value": 17, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "type_index", "type": "varuint32"}, {"name": "reserved", "type": "varuint1"}], "description": "call a function indirect with an expected signature" },
"i32.load8_s": { "category": "memory", "value": 44, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
Modified: trunk/Source/_javascript_Core/ChangeLog (209829 => 209830)
--- trunk/Source/_javascript_Core/ChangeLog 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-12-14 21:29:14 UTC (rev 209830)
@@ -1,3 +1,63 @@
+2016-12-14 Keith Miller <[email protected]>
+
+ WebAssembly JS API: implement Global
+ https://bugs.webkit.org/show_bug.cgi?id=164133
+
+ Reviewed by Saam Barati.
+
+ This patch adds support for globals. It handles imports, exports
+ and internal globals. In the MVP only internal globals are allowed
+ to be mutable. This means we can store a C-array of 64-bit slots
+ off the instance holding them. When globals are exported to JS
+ they are done so as numbers. This means that i64 globals cannot be
+ imported or exported.
+
+ * wasm/WasmB3IRGenerator.cpp:
+ (JSC::Wasm::B3IRGenerator::B3IRGenerator):
+ (JSC::Wasm::B3IRGenerator::getGlobal):
+ (JSC::Wasm::B3IRGenerator::setGlobal):
+ (JSC::Wasm::B3IRGenerator::addCallIndirect):
+ (JSC::Wasm::parseAndCompile):
+ * wasm/WasmFormat.h:
+ * wasm/WasmFunctionParser.h:
+ (JSC::Wasm::FunctionParser<Context>::parseExpression):
+ * wasm/WasmModuleParser.cpp:
+ (JSC::Wasm::ModuleParser::parseImport):
+ (JSC::Wasm::ModuleParser::parseGlobal):
+ (JSC::Wasm::ModuleParser::parseExport):
+ (JSC::Wasm::ModuleParser::parseElement):
+ (JSC::Wasm::ModuleParser::parseInitExpr):
+ (JSC::Wasm::ModuleParser::parseGlobalType):
+ (JSC::Wasm::ModuleParser::parseData):
+ * wasm/WasmModuleParser.h:
+ * wasm/WasmParser.h:
+ (JSC::Wasm::Parser::parseVarInt32):
+ (JSC::Wasm::Parser::parseVarInt64):
+ (JSC::Wasm::Parser::parseUInt64):
+ * wasm/WasmValidate.cpp:
+ (JSC::Wasm::Validate::hasMemory):
+ (JSC::Wasm::Validate::Validate):
+ (JSC::Wasm::Validate::getGlobal):
+ (JSC::Wasm::Validate::setGlobal):
+ (JSC::Wasm::validateFunction):
+ * wasm/generateWasmOpsHeader.py:
+ * wasm/js/JSWebAssemblyInstance.cpp:
+ (JSC::JSWebAssemblyInstance::create):
+ (JSC::JSWebAssemblyInstance::finishCreation):
+ (JSC::JSWebAssemblyInstance::visitChildren):
+ * wasm/js/JSWebAssemblyInstance.h:
+ (JSC::JSWebAssemblyInstance::loadI32Global):
+ (JSC::JSWebAssemblyInstance::loadI64Global):
+ (JSC::JSWebAssemblyInstance::loadF32Global):
+ (JSC::JSWebAssemblyInstance::loadF64Global):
+ (JSC::JSWebAssemblyInstance::setGlobal):
+ (JSC::JSWebAssemblyInstance::offsetOfGlobals):
+ * wasm/js/WebAssemblyInstanceConstructor.cpp:
+ (JSC::constructJSWebAssemblyInstance):
+ * wasm/js/WebAssemblyModuleRecord.cpp:
+ (JSC::WebAssemblyModuleRecord::finishCreation):
+ (JSC::WebAssemblyModuleRecord::link):
+
2016-12-14 Filip Pizlo <[email protected]>
Unreviewed, re-enable concurrent GC on ARM64 now that the most likely culprit of the memory
Modified: trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp (209829 => 209830)
--- trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp 2016-12-14 21:29:14 UTC (rev 209830)
@@ -137,7 +137,7 @@
static constexpr ExpressionType emptyExpression = nullptr;
- B3IRGenerator(VM&, const MemoryInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&, const ImmutableFunctionIndexSpace&);
+ B3IRGenerator(VM&, const ModuleInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&, const ImmutableFunctionIndexSpace&);
bool WARN_UNUSED_RETURN addArguments(const Vector<Type>&);
bool WARN_UNUSED_RETURN addLocal(Type, uint32_t);
@@ -147,6 +147,10 @@
bool WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
bool WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
+ // Globals
+ bool WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result);
+ bool WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value);
+
// Memory
bool WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
bool WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
@@ -190,6 +194,7 @@
VM& m_vm;
const ImmutableFunctionIndexSpace& m_functionIndexSpace;
+ const ModuleInformation& m_info;
Procedure& m_proc;
BasicBlock* m_currentBlock;
Vector<Variable*> m_locals;
@@ -197,12 +202,14 @@
GPRReg m_memoryBaseGPR;
GPRReg m_memorySizeGPR;
Value* m_zeroValues[numTypes];
- Value* m_functionIndexSpaceValue;
+ Value* m_instanceValue;
+
};
-B3IRGenerator::B3IRGenerator(VM& vm, const MemoryInformation& memory, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ImmutableFunctionIndexSpace& functionIndexSpace)
+B3IRGenerator::B3IRGenerator(VM& vm, const ModuleInformation& info, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ImmutableFunctionIndexSpace& functionIndexSpace)
: m_vm(vm)
, m_functionIndexSpace(functionIndexSpace)
+ , m_info(info)
, m_proc(procedure)
, m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls)
{
@@ -222,13 +229,13 @@
}
}
- if (!!memory) {
- m_memoryBaseGPR = memory.pinnedRegisters().baseMemoryPointer;
+ if (!!info.memory) {
+ m_memoryBaseGPR = info.memory.pinnedRegisters().baseMemoryPointer;
m_proc.pinRegister(m_memoryBaseGPR);
- ASSERT(!memory.pinnedRegisters().sizeRegisters[0].sizeOffset);
- m_memorySizeGPR = memory.pinnedRegisters().sizeRegisters[0].sizeRegister;
- for (const PinnedSizeRegisterInfo& info : memory.pinnedRegisters().sizeRegisters)
- m_proc.pinRegister(info.sizeRegister);
+ 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);
m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR, unsigned) {
AllowMacroScratchRegisterUsage allowScratch(jit);
@@ -271,7 +278,8 @@
wasmCallingConvention().setupFrameInPrologue(&compilation->wasmCalleeMoveLocation, m_proc, Origin(), m_currentBlock);
- m_functionIndexSpaceValue = m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), functionIndexSpace.buffer.get());
+ m_instanceValue = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(),
+ m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), &m_vm.topJSWebAssemblyInstance));
}
Value* B3IRGenerator::zeroForType(Type type)
@@ -325,6 +333,21 @@
return true;
}
+bool B3IRGenerator::getGlobal(uint32_t index, ExpressionType& result)
+{
+ Value* globalsArray = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), m_instanceValue, JSWebAssemblyInstance::offsetOfGlobals());
+ result = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, toB3Type(m_info.globals[index].type), Origin(), globalsArray, index * sizeof(Register));
+ return true;
+}
+
+bool B3IRGenerator::setGlobal(uint32_t index, ExpressionType value)
+{
+ ASSERT(toB3Type(m_info.globals[index].type) == value->type());
+ Value* globalsArray = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), m_instanceValue, JSWebAssemblyInstance::offsetOfGlobals());
+ m_currentBlock->appendNew<MemoryValue>(m_proc, Store, Origin(), value, globalsArray, index * sizeof(Register));
+ return true;
+}
+
inline Value* B3IRGenerator::emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOperation)
{
ASSERT(m_memoryBaseGPR && m_memorySizeGPR);
@@ -674,10 +697,8 @@
ExpressionType callableFunctionBuffer;
ExpressionType callableFunctionBufferSize;
{
- ExpressionType topInstance = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(),
- m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), &m_vm.topJSWebAssemblyInstance));
ExpressionType table = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(),
- topInstance, JSWebAssemblyInstance::offsetOfTable());
+ m_instanceValue, JSWebAssemblyInstance::offsetOfTable());
callableFunctionBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(),
table, JSWebAssemblyTable::offsetOfFunctions());
callableFunctionBufferSize = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, Origin(),
@@ -872,7 +893,7 @@
auto result = std::make_unique<WasmInternalFunction>();
Procedure procedure;
- B3IRGenerator context(vm, info.memory, procedure, result.get(), unlinkedWasmToWasmCalls, functionIndexSpace);
+ B3IRGenerator context(vm, info, procedure, result.get(), unlinkedWasmToWasmCalls, functionIndexSpace);
FunctionParser<B3IRGenerator> parser(context, functionStart, functionLength, signature, functionIndexSpace, info);
if (!parser.parse())
RELEASE_ASSERT_NOT_REACHED();
Modified: trunk/Source/_javascript_Core/wasm/WasmFormat.h (209829 => 209830)
--- trunk/Source/_javascript_Core/wasm/WasmFormat.h 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/wasm/WasmFormat.h 2016-12-14 21:29:14 UTC (rev 209830)
@@ -93,7 +93,7 @@
Type returnType;
Vector<Type> arguments;
};
-
+
struct Import {
Identifier module;
Identifier field;
@@ -104,12 +104,26 @@
struct Export {
Identifier field;
External::Kind kind;
- union {
- uint32_t functionIndex;
- // FIXME implement Table https://bugs.webkit.org/show_bug.cgi?id=165782
- // FIXME implement Memory https://bugs.webkit.org/show_bug.cgi?id=165671
- // FIXME implement Global https://bugs.webkit.org/show_bug.cgi?id=164133
+ unsigned kindIndex; // Index in the vector of the corresponding kind.
+};
+
+struct Global {
+ enum Mutability : uint8_t {
+ // FIXME auto-generate this. https://bugs.webkit.org/show_bug.cgi?id=165231
+ Mutable = 1,
+ Immutable = 0
};
+
+ enum InitializationType {
+ IsImport,
+ FromGlobalImport,
+ FromExpression
+ };
+
+ Mutability mutability;
+ Type type;
+ InitializationType initializationType { IsImport };
+ uint64_t initialBitsOrImportNumber { 0 };
};
struct FunctionLocationInBinary {
@@ -184,7 +198,6 @@
Vector<Signature> signatures;
Vector<Import> imports;
Vector<Signature*> importFunctions;
- // FIXME implement import Global https://bugs.webkit.org/show_bug.cgi?id=164133
Vector<Signature*> internalFunctionSignatures;
MemoryInformation memory;
Vector<Export> exports;
@@ -192,6 +205,8 @@
Vector<Segment::Ptr> data;
Vector<Element> elements;
TableInformation tableInformation;
+ Vector<Global> globals;
+ unsigned firstInternalGlobal { 0 };
~ModuleInformation();
};
Modified: trunk/Source/_javascript_Core/wasm/WasmFunctionParser.h (209829 => 209830)
--- trunk/Source/_javascript_Core/wasm/WasmFunctionParser.h 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/wasm/WasmFunctionParser.h 2016-12-14 21:29:14 UTC (rev 209830)
@@ -351,6 +351,28 @@
return m_context.setLocal(index, m_expressionStack.last());
}
+ case OpType::GetGlobal: {
+ uint32_t index;
+ if (!parseVarUInt32(index))
+ return false;
+ ExpressionType result;
+ if (!m_context.getGlobal(index, result))
+ return false;
+
+ m_expressionStack.append(result);
+ return true;
+ }
+
+ case OpType::SetGlobal: {
+ uint32_t index;
+ if (!parseVarUInt32(index))
+ return false;
+ ExpressionType value;
+ if (!popExpressionStack(value))
+ return false;
+ return m_context.setGlobal(index, value);
+ }
+
case OpType::Call: {
uint32_t functionIndex;
if (!parseVarUInt32(functionIndex))
@@ -563,12 +585,9 @@
case OpType::GrowMemory:
case OpType::CurrentMemory:
- case OpType::GetGlobal:
- case OpType::SetGlobal: {
// FIXME: Not yet implemented.
return false;
}
- }
ASSERT_NOT_REACHED();
}
Modified: trunk/Source/_javascript_Core/wasm/WasmModuleParser.cpp (209829 => 209830)
--- trunk/Source/_javascript_Core/wasm/WasmModuleParser.cpp 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/wasm/WasmModuleParser.cpp 2016-12-14 21:29:14 UTC (rev 209830)
@@ -217,6 +217,7 @@
uint32_t importCount;
if (!parseVarUInt32(importCount)
|| importCount == std::numeric_limits<uint32_t>::max()
+ || !m_module->globals.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.
|| !m_module->imports.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.
|| !m_module->importFunctions.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.
|| !m_functionIndexSpace.tryReserveCapacity(importCount)) // FIXME this over-allocates when we fix the FIXMEs below. We'll allocate some more here when we know how many functions to expect.
@@ -263,8 +264,15 @@
break;
}
case External::Global: {
- // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
- // In the MVP, only immutable global variables can be imported.
+ Global global;
+ if (!parseGlobalType(global))
+ return false;
+
+ if (global.mutability == Global::Mutable)
+ return false;
+
+ imp.kindIndex = m_module->globals.size();
+ m_module->globals.uncheckedAppend(WTFMove(global));
break;
}
}
@@ -272,6 +280,7 @@
m_module->imports.uncheckedAppend(imp);
}
+ m_module->firstInternalGlobal = m_module->globals.size();
return true;
}
@@ -423,8 +432,52 @@
bool ModuleParser::parseGlobal()
{
- // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
- RELEASE_ASSERT_NOT_REACHED();
+ uint32_t globalCount;
+ if (!parseVarUInt32(globalCount))
+ return false;
+ if (!m_module->globals.tryReserveCapacity(globalCount + m_module->firstInternalGlobal))
+ return false;
+
+ for (uint32_t globalIndex = 0; globalIndex < globalCount; ++globalIndex) {
+ Global global;
+ if (!parseGlobalType(global))
+ return false;
+
+ uint8_t initOpcode;
+ if (!parseInitExpr(initOpcode, global.initialBitsOrImportNumber))
+ return false;
+
+ global.initializationType = Global::FromExpression;
+ Type typeForInitOpcode;
+ switch (initOpcode) {
+ case I32Const:
+ typeForInitOpcode = I32;
+ break;
+ case I64Const:
+ typeForInitOpcode = I64;
+ break;
+ case F32Const:
+ typeForInitOpcode = F32;
+ break;
+ case F64Const:
+ typeForInitOpcode = F64;
+ break;
+ case GetGlobal:
+ if (global.initialBitsOrImportNumber >= m_module->firstInternalGlobal)
+ return false;
+ typeForInitOpcode = m_module->globals[global.initialBitsOrImportNumber].type;
+ global.initializationType = Global::FromGlobalImport;
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ if (typeForInitOpcode != global.type)
+ return false;
+
+ m_module->globals.uncheckedAppend(WTFMove(global));
+ }
+
return true;
}
@@ -448,10 +501,12 @@
if (!parseExternalKind(exp.kind))
return false;
+ if (!parseVarUInt32(exp.kindIndex))
+ return false;
+
switch (exp.kind) {
case External::Function: {
- if (!parseVarUInt32(exp.functionIndex)
- || exp.functionIndex >= m_functionIndexSpace.size())
+ if (exp.kindIndex >= m_functionIndexSpace.size())
return false;
break;
}
@@ -464,8 +519,11 @@
break;
}
case External::Global: {
- // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
- // In the MVP, only immutable global variables can be exported.
+ if (exp.kindIndex >= m_module->globals.size())
+ return false;
+
+ if (m_module->globals[exp.kindIndex].mutability != Global::Immutable)
+ return false;
break;
}
}
@@ -509,10 +567,14 @@
if (tableIndex != 0)
return false;
- uint32_t offset;
- if (!parseInitExpr(offset))
+ uint64_t offset;
+ uint8_t initOpcode;
+ if (!parseInitExpr(initOpcode, offset))
return false;
+ if (initOpcode != OpType::I32Const)
+ return false;
+
uint32_t indexCount;
if (!parseVarUInt32(indexCount))
return false;
@@ -577,22 +639,82 @@
return true;
}
-bool ModuleParser::parseInitExpr(uint32_t& value)
+bool ModuleParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber)
{
- // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700
- // For now we only handle i32.const as offset.
+ if (!parseUInt8(opcode))
+ return false;
- uint8_t opcode;
+ switch (opcode) {
+ case I32Const: {
+ int32_t constant;
+ if (!parseVarInt32(constant))
+ return false;
+ bitsOrImportNumber = static_cast<uint64_t>(constant);
+ break;
+ }
+
+ case I64Const: {
+ int64_t constant;
+ if (!parseVarInt64(constant))
+ return false;
+ bitsOrImportNumber = constant;
+ break;
+ }
+
+ case F32Const: {
+ uint32_t constant;
+ if (!parseUInt32(constant))
+ return false;
+ bitsOrImportNumber = constant;
+ break;
+ }
+
+ case F64Const: {
+ uint64_t constant;
+ if (!parseUInt64(constant))
+ return false;
+ bitsOrImportNumber = constant;
+ break;
+ }
+
+ case GetGlobal: {
+ uint32_t index;
+ if (!parseVarUInt32(index))
+ return false;
+
+ if (index >= m_module->imports.size())
+ return false;
+ const Import& import = m_module->imports[index];
+ if (m_module->imports[index].kind != External::Global
+ || import.kindIndex >= m_module->firstInternalGlobal)
+ return false;
+
+ ASSERT(m_module->globals[import.kindIndex].mutability == Global::Immutable);
+
+ bitsOrImportNumber = index;
+ break;
+ }
+
+ default:
+ return false;
+ }
+
uint8_t endOpcode;
- if (!parseUInt8(opcode)
- || opcode != Wasm::I32Const
- || !parseVarUInt32(value)
- || !parseUInt8(endOpcode)
- || endOpcode != Wasm::End)
+ if (!parseUInt8(endOpcode) || endOpcode != OpType::End)
return false;
+
return true;
}
+bool ModuleParser::parseGlobalType(Global& global)
+{
+ uint8_t mutability;
+ if (!parseValueType(global.type) || !parseVarUInt1(mutability))
+ return false;
+ global.mutability = static_cast<Global::Mutability>(mutability);
+ return true;
+}
+
bool ModuleParser::parseData()
{
uint32_t segmentCount;
@@ -607,14 +729,19 @@
if (verbose)
dataLogLn(" segment #", segmentNumber);
uint32_t index;
- uint32_t offset;
+ uint64_t offset;
+ uint8_t initOpcode;
uint32_t dataByteLength;
if (!parseVarUInt32(index)
|| index)
return false;
- if (!parseInitExpr(offset))
+ if (!parseInitExpr(initOpcode, offset))
return false;
+
+ if (initOpcode != OpType::I32Const)
+ return false;
+
if (verbose)
dataLogLn(" offset: ", offset);
Modified: trunk/Source/_javascript_Core/wasm/WasmModuleParser.h (209829 => 209830)
--- trunk/Source/_javascript_Core/wasm/WasmModuleParser.h 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/wasm/WasmModuleParser.h 2016-12-14 21:29:14 UTC (rev 209830)
@@ -74,6 +74,8 @@
}
private:
+ bool parseGlobalType(Global&);
+
#define WASM_SECTION_DECLARE_PARSER(NAME, ID, DESCRIPTION) bool WARN_UNUSED_RETURN parse ## NAME();
FOR_EACH_WASM_SECTION(WASM_SECTION_DECLARE_PARSER)
#undef WASM_SECTION_DECLARE_PARSER
@@ -81,7 +83,7 @@
bool WARN_UNUSED_RETURN parseMemoryHelper(bool isImport);
bool WARN_UNUSED_RETURN parseTableHelper(bool isImport);
bool WARN_UNUSED_RETURN parseResizableLimits(uint32_t& initial, std::optional<uint32_t>& maximum);
- bool WARN_UNUSED_RETURN parseInitExpr(uint32_t&);
+ bool WARN_UNUSED_RETURN parseInitExpr(uint8_t&, uint64_t&);
VM* m_vm;
std::unique_ptr<ModuleInformation> m_module;
Modified: trunk/Source/_javascript_Core/wasm/WasmParser.h (209829 => 209830)
--- trunk/Source/_javascript_Core/wasm/WasmParser.h 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/wasm/WasmParser.h 2016-12-14 21:29:14 UTC (rev 209830)
@@ -51,9 +51,13 @@
bool WARN_UNUSED_RETURN parseUInt7(uint8_t&);
bool WARN_UNUSED_RETURN parseUInt8(uint8_t&);
bool WARN_UNUSED_RETURN parseUInt32(uint32_t&);
+ bool WARN_UNUSED_RETURN parseUInt64(uint64_t&);
bool WARN_UNUSED_RETURN parseVarUInt32(uint32_t&);
bool WARN_UNUSED_RETURN parseVarUInt64(uint64_t&);
+ bool WARN_UNUSED_RETURN parseVarInt32(int32_t&);
+ bool WARN_UNUSED_RETURN parseVarInt64(int64_t&);
+
bool WARN_UNUSED_RETURN parseResultType(Type&);
bool WARN_UNUSED_RETURN parseValueType(Type&);
bool WARN_UNUSED_RETURN parseExternalKind(External::Kind&);
@@ -124,6 +128,16 @@
return WTF::LEBDecoder::decodeUInt64(m_source, m_sourceLength, m_offset, result);
}
+ALWAYS_INLINE bool Parser::parseVarInt32(int32_t& result)
+{
+ return WTF::LEBDecoder::decodeInt32(m_source, m_sourceLength, m_offset, result);
+}
+
+ALWAYS_INLINE bool Parser::parseVarInt64(int64_t& result)
+{
+ return WTF::LEBDecoder::decodeInt64(m_source, m_sourceLength, m_offset, result);
+}
+
ALWAYS_INLINE bool Parser::parseUInt32(uint32_t& result)
{
if (length() < 4 || m_offset > length() - 4)
@@ -133,6 +147,15 @@
return true;
}
+ALWAYS_INLINE bool Parser::parseUInt64(uint64_t& result)
+{
+ if (length() < 8 || m_offset > length() - 8)
+ return false;
+ result = *reinterpret_cast<const uint64_t*>(source() + m_offset);
+ m_offset += 8;
+ return true;
+}
+
ALWAYS_INLINE bool Parser::parseUInt8(uint8_t& result)
{
if (m_offset >= length())
Modified: trunk/Source/_javascript_Core/wasm/WasmValidate.cpp (209829 => 209830)
--- trunk/Source/_javascript_Core/wasm/WasmValidate.cpp 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/wasm/WasmValidate.cpp 2016-12-14 21:29:14 UTC (rev 209830)
@@ -86,6 +86,10 @@
bool WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
bool WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
+ // Globals
+ bool WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result);
+ bool WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value);
+
// Memory
bool WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
bool WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
@@ -116,13 +120,13 @@
void dump(const Vector<ControlEntry>& controlStack, const ExpressionList& expressionStack);
- bool hasMemory() const { return !!m_memory; }
+ bool hasMemory() const { return !!m_module.memory; }
void setErrorMessage(String&& message) { ASSERT(m_errorMessage.isNull()); m_errorMessage = WTFMove(message); }
String errorMessage() const { return m_errorMessage; }
- Validate(ExpressionType returnType, const MemoryInformation& memory)
+ Validate(ExpressionType returnType, const ModuleInformation& module)
: m_returnType(returnType)
- , m_memory(memory)
+ , m_module(module)
{
}
@@ -135,7 +139,7 @@
ExpressionType m_returnType;
Vector<Type> m_locals;
String m_errorMessage;
- const MemoryInformation& m_memory;
+ const ModuleInformation& m_module;
};
bool Validate::addArguments(const Vector<Type>& args)
@@ -180,6 +184,38 @@
return false;
}
+bool Validate::getGlobal(uint32_t index, ExpressionType& result)
+{
+ if (index < m_module.globals.size()) {
+ result = m_module.globals[index].type;
+ ASSERT(isValueType(result));
+ return true;
+ }
+ m_errorMessage = ASCIILiteral("Attempt to use unknown global.");
+ return false;
+}
+
+bool Validate::setGlobal(uint32_t index, ExpressionType value)
+{
+ if (index >= m_module.globals.size()) {
+ m_errorMessage = ASCIILiteral("Attempt to use unknown global.");
+ return false;
+ }
+
+ if (m_module.globals[index].mutability == Global::Immutable) {
+ m_errorMessage = ASCIILiteral("Attempt to store to immutable global.");
+ return false;
+ }
+
+ ExpressionType globalType = m_module.globals[index].type;
+ ASSERT(isValueType(globalType));
+ if (globalType == value)
+ return true;
+
+ m_errorMessage = makeString("Attempt to set global with type: ", toString(globalType), " with a variable of type: ", toString(value));
+ return false;
+}
+
Validate::ControlType Validate::addBlock(Type signature)
{
return ControlData(BlockType::Block, signature);
@@ -401,10 +437,10 @@
// Think of this as penance for the sin of bad error messages.
}
-String validateFunction(const uint8_t* source, size_t length, const Signature* signature, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& info)
+String validateFunction(const uint8_t* source, size_t length, const Signature* signature, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& module)
{
- Validate context(signature->returnType, info.memory);
- FunctionParser<Validate> validator(context, source, length, signature, functionIndexSpace, info);
+ Validate context(signature->returnType, module);
+ FunctionParser<Validate> validator(context, source, length, signature, functionIndexSpace, module);
if (!validator.parse()) {
// FIXME: add better location information here. see: https://bugs.webkit.org/show_bug.cgi?id=164288
Modified: trunk/Source/_javascript_Core/wasm/generateWasmOpsHeader.py (209829 => 209830)
--- trunk/Source/_javascript_Core/wasm/generateWasmOpsHeader.py 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/wasm/generateWasmOpsHeader.py 2016-12-14 21:29:14 UTC (rev 209830)
@@ -150,7 +150,7 @@
}
#undef CREATE_CASE
-#define CREATE_CASE(name, id, b3type, inc) case name: return "name";
+#define CREATE_CASE(name, id, b3type, inc) case name: return #name;
inline const char* toString(Type type)
{
switch (type) {
Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.cpp (209829 => 209830)
--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.cpp 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.cpp 2016-12-14 21:29:14 UTC (rev 209830)
@@ -38,9 +38,10 @@
namespace JSC {
-JSWebAssemblyInstance* JSWebAssemblyInstance::create(VM& vm, Structure* structure, JSWebAssemblyModule* module, JSModuleNamespaceObject* moduleNamespaceObject, unsigned numImportFunctions)
+JSWebAssemblyInstance* JSWebAssemblyInstance::create(VM& vm, Structure* structure, JSWebAssemblyModule* module, JSModuleNamespaceObject* moduleNamespaceObject)
{
- auto* instance = new (NotNull, allocateCell<JSWebAssemblyInstance>(vm.heap, allocationSize(numImportFunctions))) JSWebAssemblyInstance(vm, structure, numImportFunctions);
+ // FIXME: These objects could be pretty big we should try to throw OOM here.
+ auto* instance = new (NotNull, allocateCell<JSWebAssemblyInstance>(vm.heap, allocationSize(module->moduleInformation().importFunctions.size()))) JSWebAssemblyInstance(vm, structure, module->moduleInformation().importFunctions.size());
instance->finishCreation(vm, module, moduleNamespaceObject);
return instance;
}
@@ -61,6 +62,11 @@
{
Base::finishCreation(vm);
ASSERT(inherits(info()));
+
+ const size_t extraMemorySize = module->moduleInformation().globals.size() * sizeof(Register);
+ m_globals = MallocPtr<uint64_t>::malloc(extraMemorySize);
+ heap()->reportExtraMemoryAllocated(extraMemorySize);
+
m_module.set(vm, this, module);
m_moduleNamespaceObject.set(vm, this, moduleNamespaceObject);
putDirect(vm, Identifier::fromString(&vm, "exports"), moduleNamespaceObject, None);
@@ -81,6 +87,7 @@
visitor.append(&thisObject->m_moduleNamespaceObject);
visitor.append(&thisObject->m_memory);
visitor.append(&thisObject->m_table);
+ visitor.reportExtraMemoryVisited(thisObject->module()->moduleInformation().globals.size());
for (unsigned i = 0; i < thisObject->m_numImportFunctions; ++i)
visitor.append(thisObject->importFunction(i));
}
Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.h (209829 => 209830)
--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.h 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.h 2016-12-14 21:29:14 UTC (rev 209830)
@@ -42,7 +42,7 @@
typedef JSDestructibleObject Base;
- static JSWebAssemblyInstance* create(VM&, Structure*, JSWebAssemblyModule*, JSModuleNamespaceObject*, unsigned);
+ static JSWebAssemblyInstance* create(VM&, Structure*, JSWebAssemblyModule*, JSModuleNamespaceObject*);
static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
DECLARE_INFO;
@@ -75,6 +75,12 @@
JSWebAssemblyTable* table() { return m_table.get(); }
void setTable(VM& vm, JSWebAssemblyTable* table) { m_table.set(vm, this, table); }
+ int32_t loadI32Global(unsigned i) const { return m_globals.get()[i]; }
+ int64_t loadI64Global(unsigned i) const { return m_globals.get()[i]; }
+ float loadF32Global(unsigned i) const { return bitwise_cast<float>(loadI32Global(i)); }
+ double loadF64Global(unsigned i) const { return bitwise_cast<double>(loadI64Global(i)); }
+ void setGlobal(unsigned i, int64_t bits) { m_globals.get()[i] = bits; }
+
static size_t offsetOfImportFunction(unsigned idx)
{
return offsetOfImportFunctions() + sizeof(WriteBarrier<JSCell>) * idx;
@@ -81,9 +87,10 @@
}
static ptrdiff_t offsetOfTable() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_table); }
+ static ptrdiff_t offsetOfGlobals() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_globals); }
protected:
- JSWebAssemblyInstance(VM&, Structure*, unsigned);
+ JSWebAssemblyInstance(VM&, Structure*, unsigned numImportFunctions);
void finishCreation(VM&, JSWebAssemblyModule*, JSModuleNamespaceObject*);
static void destroy(JSCell*);
static void visitChildren(JSCell*, SlotVisitor&);
@@ -103,6 +110,7 @@
WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject;
WriteBarrier<JSWebAssemblyMemory> m_memory;
WriteBarrier<JSWebAssemblyTable> m_table;
+ MallocPtr<uint64_t> m_globals;
unsigned m_numImportFunctions;
};
Modified: trunk/Source/_javascript_Core/wasm/js/WebAssemblyInstanceConstructor.cpp (209829 => 209830)
--- trunk/Source/_javascript_Core/wasm/js/WebAssemblyInstanceConstructor.cpp 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/wasm/js/WebAssemblyInstanceConstructor.cpp 2016-12-14 21:29:14 UTC (rev 209830)
@@ -81,15 +81,14 @@
Structure* instanceStructure = InternalFunction::createSubclassStructure(exec, exec->newTarget(), globalObject->WebAssemblyInstanceStructure());
RETURN_IF_EXCEPTION(throwScope, { });
- JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, instanceStructure, jsModule, moduleRecord->getModuleNamespace(exec), moduleInformation.imports.size());
+ JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, instanceStructure, jsModule, moduleRecord->getModuleNamespace(exec));
RETURN_IF_EXCEPTION(throwScope, { });
// 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.
unsigned numImportFunctions = 0;
+ unsigned numImportGlobals = 0;
- // FIXME implement Global https://bugs.webkit.org/show_bug.cgi?id=164133
-
bool hasMemoryImport = false;
bool hasTableImport = false;
// For each import i in module.imports:
@@ -195,11 +194,26 @@
}
case Wasm::External::Global: {
// 5. If i is a global import:
- // FIXME implement Global https://bugs.webkit.org/show_bug.cgi?id=164133
// i. If i is not an immutable global, throw a TypeError.
+ ASSERT(moduleInformation.globals[import.kindIndex].mutability == Wasm::Global::Immutable);
// ii. If Type(v) is not Number, throw a TypeError.
+ if (!value.isNumber())
+ return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("imported global must be a number"), defaultSourceAppender, runtimeTypeForValue(value))));
// iii. Append ToWebAssemblyValue(v) to imports.
- RELEASE_ASSERT_NOT_REACHED();
+ switch (moduleInformation.globals[import.kindIndex].type) {
+ case Wasm::I32:
+ instance->setGlobal(numImportGlobals++, value.toInt32(exec));
+ break;
+ case Wasm::F32:
+ instance->setGlobal(numImportGlobals++, bitwise_cast<uint32_t>(value.toFloat(exec)));
+ break;
+ case Wasm::F64:
+ instance->setGlobal(numImportGlobals++, bitwise_cast<uint64_t>(value.asNumber()));
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ ASSERT(!throwScope.exception());
break;
}
}
@@ -241,8 +255,23 @@
}
}
+ // Globals
+ {
+ ASSERT(numImportGlobals == moduleInformation.firstInternalGlobal);
+ for (size_t globalIndex = numImportGlobals; globalIndex < moduleInformation.globals.size(); ++globalIndex) {
+ const auto& global = moduleInformation.globals[globalIndex];
+ ASSERT(global.initializationType != Wasm::Global::IsImport);
+ if (global.initializationType == Wasm::Global::FromGlobalImport) {
+ ASSERT(global.initialBitsOrImportNumber < numImportGlobals);
+ instance->setGlobal(globalIndex, instance->loadI64Global(global.initialBitsOrImportNumber));
+ } else
+ instance->setGlobal(globalIndex, global.initialBitsOrImportNumber);
+ }
+ }
+
moduleRecord->link(exec, instance);
RETURN_IF_EXCEPTION(throwScope, { });
+
if (verbose)
moduleRecord->dump();
JSValue startResult = moduleRecord->evaluate(exec);
Modified: trunk/Source/_javascript_Core/wasm/js/WebAssemblyModuleRecord.cpp (209829 => 209830)
--- trunk/Source/_javascript_Core/wasm/js/WebAssemblyModuleRecord.cpp 2016-12-14 21:18:02 UTC (rev 209829)
+++ trunk/Source/_javascript_Core/wasm/js/WebAssemblyModuleRecord.cpp 2016-12-14 21:29:14 UTC (rev 209830)
@@ -85,8 +85,8 @@
break;
}
case Wasm::External::Global: {
- // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
// In the MVP, only immutable global variables can be exported.
+ addExportEntry(ExportEntry::createLocal(exp.field, exp.field));
break;
}
}
@@ -128,7 +128,7 @@
// 1. If e is a closure c:
// i. If there is an Exported Function Exotic Object func in funcs whose func.[[Closure]] equals c, then return func.
// ii. (Note: At most one wrapper is created for any closure, so func is unique, even if there are multiple occurrances in the list. Moreover, if the item was an import that is already an Exported Function Exotic Object, then the original function object will be found. For imports that are regular JS functions, a new wrapper will be created.)
- if (exp.functionIndex < importCount) {
+ if (exp.kindIndex < importCount) {
// FIXME Implement re-exporting an import. https://bugs.webkit.org/show_bug.cgi?id=165510
RELEASE_ASSERT_NOT_REACHED();
}
@@ -136,12 +136,12 @@
// a. Let func be an Exported Function Exotic Object created from c.
// b. Append func to funcs.
// c. Return func.
- JSWebAssemblyCallee* jsEntrypointCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(exp.functionIndex);
- JSWebAssemblyCallee* wasmEntrypointCallee = module->wasmEntrypointCalleeFromFunctionIndexSpace(exp.functionIndex);
- Wasm::Signature* signature = module->signatureForFunctionIndexSpace(exp.functionIndex);
+ JSWebAssemblyCallee* jsEntrypointCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
+ JSWebAssemblyCallee* wasmEntrypointCallee = module->wasmEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
+ Wasm::Signature* signature = module->signatureForFunctionIndexSpace(exp.kindIndex);
WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->arguments.size(), exp.field.string(), instance, jsEntrypointCallee, wasmEntrypointCallee, signature);
exportedValue = function;
- if (hasStart && startFunctionIndexSpace == exp.functionIndex)
+ if (hasStart && startFunctionIndexSpace == exp.kindIndex)
m_startFunction.set(vm, this, function);
break;
}
@@ -153,9 +153,28 @@
// FIXME: https://bugs.webkit.org/show_bug.cgi?id=165671
break;
}
+
case Wasm::External::Global: {
- // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
- // In the MVP, only immutable global variables can be exported.
+ // Assert: the global is immutable by MVP validation constraint.
+ const Wasm::Global& global = moduleInformation.globals[exp.kindIndex];
+ ASSERT(global.mutability == Wasm::Global::Immutable);
+ // Return ToJSValue(v).
+ switch (global.type) {
+ case Wasm::I32:
+ exportedValue = JSValue(instance->loadI32Global(exp.kindIndex));
+ break;
+
+ case Wasm::F32:
+ exportedValue = JSValue(instance->loadF32Global(exp.kindIndex));
+ break;
+
+ case Wasm::F64:
+ exportedValue = JSValue(instance->loadF64Global(exp.kindIndex));
+ break;
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
break;
}
}