Diff
Modified: trunk/JSTests/ChangeLog (246576 => 246577)
--- trunk/JSTests/ChangeLog 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/JSTests/ChangeLog 2019-06-19 01:18:43 UTC (rev 246577)
@@ -1,5 +1,19 @@
2019-06-18 Justin Michaud <[email protected]>
+ [WASM-References] Add support for Table.size, grow and fill instructions
+ https://bugs.webkit.org/show_bug.cgi?id=198761
+
+ Reviewed by Yusuke Suzuki.
+
+ * wasm/Builder_WebAssemblyBinary.js:
+ (const.putOp):
+ * wasm/references/table_misc.js: Added.
+ (TableSize.End.End.WebAssembly):
+ (GetLocal.0.GetLocal.1.TableGrow.End.End.WebAssembly):
+ * wasm/wasm.json:
+
+2019-06-18 Justin Michaud <[email protected]>
+
[WASM-References] Add support for multiple tables
https://bugs.webkit.org/show_bug.cgi?id=198760
Modified: trunk/JSTests/wasm/Builder_WebAssemblyBinary.js (246576 => 246577)
--- trunk/JSTests/wasm/Builder_WebAssemblyBinary.js 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/JSTests/wasm/Builder_WebAssemblyBinary.js 2019-06-19 01:18:43 UTC (rev 246577)
@@ -60,6 +60,8 @@
const putOp = (bin, op) => {
put(bin, "uint8", op.value);
+ if (WASM.description.opcode[op.name].extendedOp)
+ put(bin, "uint8", WASM.description.opcode[op.name].extendedOp);
if (op.arguments.length !== 0)
throw new Error(`Unimplemented: arguments`); // FIXME https://bugs.webkit.org/show_bug.cgi?id=162706
Added: trunk/JSTests/wasm/references/table_misc.js (0 => 246577)
--- trunk/JSTests/wasm/references/table_misc.js (rev 0)
+++ trunk/JSTests/wasm/references/table_misc.js 2019-06-19 01:18:43 UTC (rev 246577)
@@ -0,0 +1,225 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+{
+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+ .Type().End()
+ .Function().End()
+ .Table()
+ .Table({initial: 0, maximum: 0, element: "anyref"})
+ .Table({initial: 20, maximum: 30, element: "anyref"})
+ .End()
+ .Export()
+ .Function("tbl_size")
+ .Table("tbl", 1)
+ .End()
+ .Code()
+ .Function("tbl_size", { params: [], ret: "i32" })
+ .TableSize(1)
+ .End()
+ .End().WebAssembly().get()));
+ fullGC()
+
+ assert.eq($1.exports.tbl_size(), 20)
+ assert.eq($1.exports.tbl.grow(5), 20)
+ assert.eq($1.exports.tbl_size(), 25)
+ assert.eq($1.exports.tbl.get(0), null)
+ assert.eq($1.exports.tbl.get(24), null)
+}
+
+assert.throws(() => new WebAssembly.Module((new Builder())
+ .Type().End()
+ .Function().End()
+ .Export()
+ .Function("tbl_size")
+ .End()
+ .Code()
+ .Function("tbl_size", { params: [], ret: "i32" })
+ .TableSize(0)
+ .End()
+ .End().WebAssembly().get()), Error, "WebAssembly.Module doesn't validate: table index 0 is invalid, limit is 0, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+{
+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+ .Type().End()
+ .Function().End()
+ .Table()
+ .Table({initial: 0, maximum: 0, element: "anyref"})
+ .Table({initial: 20, maximum: 30, element: "anyref"})
+ .End()
+ .Export()
+ .Function("tbl_size")
+ .Function("tbl_grow")
+ .Table("tbl", 1)
+ .End()
+ .Code()
+ .Function("tbl_size", { params: [], ret: "i32" })
+ .TableSize(1)
+ .End()
+ .Function("tbl_grow", { params: ["anyref", "i32"], ret: "i32" })
+ .GetLocal(0)
+ .GetLocal(1)
+ .TableGrow(1)
+ .End()
+ .End().WebAssembly().get()));
+ fullGC()
+
+ assert.eq($1.exports.tbl_size(), 20)
+ assert.eq($1.exports.tbl_grow("hi", 5), 20)
+ assert.eq($1.exports.tbl_size(), 25)
+ assert.eq($1.exports.tbl.get(0), null)
+ assert.eq($1.exports.tbl.get(24), "hi")
+
+ assert.eq($1.exports.tbl_grow(null, 5), 25)
+ assert.eq($1.exports.tbl.get(24), "hi")
+ assert.eq($1.exports.tbl.get(25), null)
+ assert.eq($1.exports.tbl_size(), 30)
+ assert.eq($1.exports.tbl_grow(null, 0), -1)
+ assert.eq($1.exports.tbl_grow(null, 5), -1)
+ assert.eq($1.exports.tbl_grow(null, 0), -1)
+}
+
+assert.throws(() => new WebAssembly.Module((new Builder())
+ .Type().End()
+ .Function().End()
+ .Code()
+ .Function("tbl_grow", { params: ["anyref", "i32"], ret: "i32" })
+ .GetLocal(0)
+ .GetLocal(1)
+ .TableGrow(0)
+ .End()
+ .End().WebAssembly().get()), Error, "WebAssembly.Module doesn't validate: table index 0 is invalid, limit is 0, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+assert.throws(() => new WebAssembly.Module((new Builder())
+ .Type().End()
+ .Function().End()
+ .Table()
+ .Table({initial: 20, maximum: 30, element: "anyref"})
+ .End()
+ .Code()
+ .Function("tbl_grow", { params: ["anyref", "i32"], ret: "i32" })
+ .GetLocal(0)
+ .TableGrow(0)
+ .End()
+ .End().WebAssembly().get()), Error, "WebAssembly.Module doesn't parse at byte 6: can't pop empty stack in table.grow, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+assert.throws(() => new WebAssembly.Module((new Builder())
+ .Type().End()
+ .Function().End()
+ .Table()
+ .Table({initial: 20, maximum: 30, element: "anyref"})
+ .End()
+ .Code()
+ .Function("tbl_grow", { params: ["i32", "i32"], ret: "i32" })
+ .GetLocal(0)
+ .GetLocal(1)
+ .TableGrow(0)
+ .End()
+ .End().WebAssembly().get()), Error, "WebAssembly.Module doesn't validate: table.grow expects fill value of type Anyref got I32, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+{
+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+ .Type().End()
+ .Function().End()
+ .Table()
+ .Table({initial: 20, maximum: 30, element: "anyref"})
+ .End()
+ .Export()
+ .Function("tbl_size")
+ .Function("tbl_fill")
+ .Table("tbl", 0)
+ .End()
+ .Code()
+ .Function("tbl_size", { params: [], ret: "i32" })
+ .TableSize(0)
+ .End()
+ .Function("tbl_fill", { params: ["i32", "anyref", "i32"], ret: "void" })
+ .GetLocal(0)
+ .GetLocal(1)
+ .GetLocal(2)
+ .TableFill(0)
+ .End()
+ .End().WebAssembly().get()));
+ fullGC()
+
+ assert.eq($1.exports.tbl_size(), 20)
+ $1.exports.tbl_fill(1,"hi",3)
+ assert.eq($1.exports.tbl.get(0), null)
+ assert.eq($1.exports.tbl.get(1), "hi")
+ assert.eq($1.exports.tbl.get(2), "hi")
+ assert.eq($1.exports.tbl.get(3), "hi")
+ assert.eq($1.exports.tbl.get(4), null)
+
+ $1.exports.tbl_fill(0,null,1)
+ assert.eq($1.exports.tbl.get(0), null)
+ $1.exports.tbl_fill(0,null,0)
+
+ $1.exports.tbl_fill(19,"test",1)
+ assert.eq($1.exports.tbl.get(19), "test")
+ assert.eq($1.exports.tbl.get(18), null)
+
+ assert.throws(() => $1.exports.tbl_fill(20,null,0), Error, "Out of bounds table access (evaluating 'func(...args)')")
+ assert.throws(() => $1.exports.tbl_fill(20,null,1), Error, "Out of bounds table access (evaluating 'func(...args)')")
+ assert.throws(() => $1.exports.tbl_fill(19,null,2), Error, "Out of bounds table access (evaluating 'func(...args)')")
+ assert.throws(() => $1.exports.tbl_fill(4294967295,null,1), Error, "Out of bounds table access (evaluating 'func(...args)')")
+}
+
+assert.throws(() => new WebAssembly.Module((new Builder())
+ .Type().End()
+ .Function().End()
+ .Code()
+ .Function("tbl_grow", { params: ["anyref", "i32"], ret: "void" })
+ .GetLocal(1)
+ .GetLocal(0)
+ .GetLocal(1)
+ .TableFill(0)
+ .End()
+ .End().WebAssembly().get()), Error, "WebAssembly.Module doesn't validate: table index 0 is invalid, limit is 0, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+assert.throws(() => new WebAssembly.Module((new Builder())
+ .Type().End()
+ .Function().End()
+ .Table()
+ .Table({initial: 20, maximum: 30, element: "anyref"})
+ .End()
+ .Code()
+ .Function("tbl_grow", { params: ["anyref", "i32"], ret: "i32" })
+ .GetLocal(0)
+ .TableFill(0)
+ .End()
+ .End().WebAssembly().get()), Error, "WebAssembly.Module doesn't parse at byte 6: can't pop empty stack in table.fill, in function at index 0 (evaluating 'new WebAssembly.Module')")
+
+{
+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+ .Type().End()
+ .Function().End()
+ .Table()
+ .Table({initial: 0, maximum: 0, element: "anyfunc"})
+ .Table({initial: 20, maximum: 30, element: "anyfunc"})
+ .End()
+ .Export()
+ .Function("tbl_size")
+ .Function("tbl_grow")
+ .Table("tbl", 1)
+ .End()
+ .Code()
+ .Function("tbl_size", { params: [], ret: "i32" })
+ .TableSize(1)
+ .End()
+ .Function("tbl_grow", { params: ["i32"], ret: "i32" })
+ .I32Const(0)
+ .TableGet(1)
+ .GetLocal(0)
+ .TableGrow(1)
+ .End()
+ .End().WebAssembly().get()));
+ fullGC()
+
+ $1.exports.tbl.set(0, $1.exports.tbl_size);
+ assert.eq($1.exports.tbl_size(), 20)
+ assert.eq($1.exports.tbl_grow(5), 20)
+ assert.eq($1.exports.tbl_size(), 25)
+ assert.eq($1.exports.tbl.get(0), $1.exports.tbl_size)
+ assert.eq($1.exports.tbl.get(1), null)
+ assert.eq($1.exports.tbl.get(24), $1.exports.tbl_size)
+}
Modified: trunk/JSTests/wasm/wasm.json (246576 => 246577)
--- trunk/JSTests/wasm/wasm.json 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/JSTests/wasm/wasm.json 2019-06-19 01:18:43 UTC (rev 246577)
@@ -69,6 +69,9 @@
"set_global": { "category": "special", "value": 36, "return": [], "parameter": ["any"], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "write a global variable" },
"table.get": { "category": "special", "value": 37, "return": ["anyref"], "parameter": ["i32"], "immediate": [{"name": "table_index", "type": "varuint32"}], "description": "get a table value" },
"table.set": { "category": "special", "value": 38, "return": [], "parameter": ["i32", "anyref"], "immediate": [{"name": "table_index", "type": "varuint32"}], "description": "set a table value" },
+ "table.size": { "category": "exttable", "value": 252, "return": ["i32"], "parameter": [], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "get the size of a table", "extendedOp": 15 },
+ "table.grow": { "category": "exttable", "value": 252, "return": ["i32"], "parameter": ["anyref", "i32"], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "grow a table by the given delta and return the previous size, or -1 if enough space cannot be allocated", "extendedOp": 16 },
+ "table.fill": { "category": "exttable", "value": 252, "return": ["i32"], "parameter": ["i32", "anyref", "i32"], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "fill entries [i,i+n) with the given value", "extendedOp": 17 },
"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": "table_index","type": "varuint32"}],"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 (246576 => 246577)
--- trunk/Source/_javascript_Core/ChangeLog 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/Source/_javascript_Core/ChangeLog 2019-06-19 01:18:43 UTC (rev 246577)
@@ -1,3 +1,44 @@
+2019-06-18 Justin Michaud <[email protected]>
+
+ [WASM-References] Add support for Table.size, grow and fill instructions
+ https://bugs.webkit.org/show_bug.cgi?id=198761
+
+ Reviewed by Yusuke Suzuki.
+
+ Add support for Table.size, grow and fill instructions. This also required
+ adding support for two-byte opcodes to the ops generator.
+
+ * wasm/WasmAirIRGenerator.cpp:
+ (JSC::Wasm::AirIRGenerator::gAnyref):
+ (JSC::Wasm::AirIRGenerator::tmpForType):
+ (JSC::Wasm::AirIRGenerator::addTableSize):
+ (JSC::Wasm::AirIRGenerator::addTableGrow):
+ (JSC::Wasm::AirIRGenerator::addTableFill):
+ * wasm/WasmB3IRGenerator.cpp:
+ (JSC::Wasm::B3IRGenerator::addTableSize):
+ (JSC::Wasm::B3IRGenerator::addTableGrow):
+ (JSC::Wasm::B3IRGenerator::addTableFill):
+ * wasm/WasmExceptionType.h:
+ * wasm/WasmFormat.h:
+ (JSC::Wasm::TableInformation::wasmType const):
+ * wasm/WasmFunctionParser.h:
+ (JSC::Wasm::FunctionParser<Context>::parseExpression):
+ (JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression):
+ * wasm/WasmInstance.cpp:
+ (JSC::Wasm::doWasmTableGrow):
+ (JSC::Wasm::doWasmTableFill):
+ * wasm/WasmInstance.h:
+ * wasm/WasmTable.cpp:
+ (JSC::Wasm::Table::grow):
+ * wasm/WasmValidate.cpp:
+ (JSC::Wasm::Validate::addTableSize):
+ (JSC::Wasm::Validate::addTableGrow):
+ (JSC::Wasm::Validate::addTableFill):
+ * wasm/generateWasmOpsHeader.py:
+ (opcodeMacroizer):
+ (ExtTableOpType):
+ * wasm/wasm.json:
+
2019-06-18 Keith Miller <[email protected]>
Unreviewed, fix signature of currentWeakRefVersion to return an uintptr_t.
Modified: trunk/Source/_javascript_Core/wasm/WasmAirIRGenerator.cpp (246576 => 246577)
--- trunk/Source/_javascript_Core/wasm/WasmAirIRGenerator.cpp 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/Source/_javascript_Core/wasm/WasmAirIRGenerator.cpp 2019-06-19 01:18:43 UTC (rev 246577)
@@ -239,6 +239,9 @@
// Tables
PartialResult WARN_UNUSED_RETURN addTableGet(unsigned, ExpressionType& index, ExpressionType& result);
PartialResult WARN_UNUSED_RETURN addTableSet(unsigned, ExpressionType& index, ExpressionType& value);
+ PartialResult WARN_UNUSED_RETURN addTableSize(unsigned, ExpressionType& result);
+ PartialResult WARN_UNUSED_RETURN addTableGrow(unsigned, ExpressionType& fill, ExpressionType& delta, ExpressionType& result);
+ PartialResult WARN_UNUSED_RETURN addTableFill(unsigned, ExpressionType& offset, ExpressionType& fill, ExpressionType& count);
// Locals
PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
@@ -361,6 +364,8 @@
TypedTmp g32() { return { newTmp(B3::GP), Type::I32 }; }
TypedTmp g64() { return { newTmp(B3::GP), Type::I64 }; }
+ TypedTmp gAnyref() { return { newTmp(B3::GP), Type::Anyref }; }
+ TypedTmp gAnyfunc() { return { newTmp(B3::GP), Type::Anyfunc }; }
TypedTmp f32() { return { newTmp(B3::FP), Type::F32 }; }
TypedTmp f64() { return { newTmp(B3::FP), Type::F64 }; }
@@ -370,9 +375,11 @@
case Type::I32:
return g32();
case Type::I64:
+ return g64();
+ case Type::Anyfunc:
+ return gAnyfunc();
case Type::Anyref:
- case Type::Anyfunc:
- return g64();
+ return gAnyref();
case Type::F32:
return f32();
case Type::F64:
@@ -508,6 +515,8 @@
resultType = B3::Int32;
break;
case Type::I64:
+ case Type::Anyref:
+ case Type::Anyfunc:
resultType = B3::Int64;
break;
case Type::F32:
@@ -975,7 +984,7 @@
// FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>.
ASSERT(index.tmp());
ASSERT(index.type() == Type::I32);
- result = tmpForType(Type::Anyref);
+ result = tmpForType(m_info.tables[tableIndex].wasmType());
emitCCall(&getWasmTableElement, result, instanceValue(), addConstant(Type::I32, tableIndex), index);
emitCheck([&] {
@@ -1006,6 +1015,54 @@
return { };
}
+auto AirIRGenerator::addTableSize(unsigned tableIndex, ExpressionType& result) -> PartialResult
+{
+ // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>.
+ result = tmpForType(Type::I32);
+
+ int32_t (*doSize)(Instance*, unsigned) = [] (Instance* instance, unsigned tableIndex) -> int32_t {
+ return instance->table(tableIndex)->length();
+ };
+
+ emitCCall(doSize, result, instanceValue(), addConstant(Type::I32, tableIndex));
+
+ return { };
+}
+
+auto AirIRGenerator::addTableGrow(unsigned tableIndex, ExpressionType& fill, ExpressionType& delta, ExpressionType& result) -> PartialResult
+{
+ ASSERT(fill.tmp());
+ ASSERT(isSubtype(fill.type(), m_info.tables[tableIndex].wasmType()));
+ ASSERT(delta.tmp());
+ ASSERT(delta.type() == Type::I32);
+ result = tmpForType(Type::I32);
+
+ emitCCall(&doWasmTableGrow, result, instanceValue(), addConstant(Type::I32, tableIndex), fill, delta);
+
+ return { };
+}
+
+auto AirIRGenerator::addTableFill(unsigned tableIndex, ExpressionType& offset, ExpressionType& fill, ExpressionType& count) -> PartialResult
+{
+ ASSERT(fill.tmp());
+ ASSERT(isSubtype(fill.type(), m_info.tables[tableIndex].wasmType()));
+ ASSERT(offset.tmp());
+ ASSERT(offset.type() == Type::I32);
+ ASSERT(count.tmp());
+ ASSERT(count.type() == Type::I32);
+
+ auto result = tmpForType(Type::I32);
+ emitCCall(&doWasmTableFill, result, instanceValue(), addConstant(Type::I32, tableIndex), offset, fill, count);
+
+ emitCheck([&] {
+ return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::Zero), result, result);
+ }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
+ this->emitThrowException(jit, ExceptionType::OutOfBoundsTableAccess);
+ });
+
+ return { };
+}
+
auto AirIRGenerator::getLocal(uint32_t index, ExpressionType& result) -> PartialResult
{
ASSERT(m_locals[index].tmp());
Modified: trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp (246576 => 246577)
--- trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp 2019-06-19 01:18:43 UTC (rev 246577)
@@ -192,7 +192,9 @@
// Tables
PartialResult WARN_UNUSED_RETURN addTableGet(unsigned, ExpressionType& index, ExpressionType& result);
PartialResult WARN_UNUSED_RETURN addTableSet(unsigned, ExpressionType& index, ExpressionType& value);
-
+ PartialResult WARN_UNUSED_RETURN addTableSize(unsigned, ExpressionType& result);
+ PartialResult WARN_UNUSED_RETURN addTableGrow(unsigned, ExpressionType& fill, ExpressionType& delta, ExpressionType& result);
+ PartialResult WARN_UNUSED_RETURN addTableFill(unsigned, ExpressionType& offset, ExpressionType& fill, ExpressionType& count);
// Locals
PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
PartialResult WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
@@ -617,6 +619,47 @@
return { };
}
+auto B3IRGenerator::addTableSize(unsigned tableIndex, ExpressionType& result) -> PartialResult
+{
+ // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>.
+ uint32_t (*doSize)(Instance*, unsigned) = [] (Instance* instance, unsigned tableIndex) -> uint32_t {
+ return instance->table(tableIndex)->length();
+ };
+
+ result = m_currentBlock->appendNew<CCallValue>(m_proc, toB3Type(I32), origin(),
+ m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(doSize, B3CCallPtrTag)),
+ instanceValue(), m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex));
+
+ return { };
+}
+
+auto B3IRGenerator::addTableGrow(unsigned tableIndex, ExpressionType& fill, ExpressionType& delta, ExpressionType& result) -> PartialResult
+{
+ result = m_currentBlock->appendNew<CCallValue>(m_proc, toB3Type(I32), origin(),
+ m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(&doWasmTableGrow, B3CCallPtrTag)),
+ instanceValue(), m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex), fill, delta);
+
+ return { };
+}
+
+auto B3IRGenerator::addTableFill(unsigned tableIndex, ExpressionType& offset, ExpressionType& fill, ExpressionType& count) -> PartialResult
+{
+ auto result = m_currentBlock->appendNew<CCallValue>(m_proc, toB3Type(I32), origin(),
+ m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(&doWasmTableFill, B3CCallPtrTag)),
+ instanceValue(), m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex), offset, fill, count);
+
+ {
+ CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
+ m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), result, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), 0)));
+
+ check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
+ this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTableAccess);
+ });
+ }
+
+ return { };
+}
+
auto B3IRGenerator::getLocal(uint32_t index, ExpressionType& result) -> PartialResult
{
ASSERT(m_locals[index]);
Modified: trunk/Source/_javascript_Core/wasm/WasmExceptionType.h (246576 => 246577)
--- trunk/Source/_javascript_Core/wasm/WasmExceptionType.h 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/Source/_javascript_Core/wasm/WasmExceptionType.h 2019-06-19 01:18:43 UTC (rev 246577)
@@ -33,7 +33,7 @@
#define FOR_EACH_EXCEPTION(macro) \
macro(OutOfBoundsMemoryAccess, "Out of bounds memory access") \
- macro(OutOfBoundsTableAccess, "Out of bounds table access") \
+ macro(OutOfBoundsTableAccess, "Out of bounds table access") \
macro(OutOfBoundsCallIndirect, "Out of bounds call_indirect") \
macro(NullTableEntry, "call_indirect to a null table entry") \
macro(BadSignature, "call_indirect to a signature that does not match") \
Modified: trunk/Source/_javascript_Core/wasm/WasmFunctionParser.h (246576 => 246577)
--- trunk/Source/_javascript_Core/wasm/WasmFunctionParser.h 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/Source/_javascript_Core/wasm/WasmFunctionParser.h 2019-06-19 01:18:43 UTC (rev 246577)
@@ -303,6 +303,43 @@
return { };
}
+ case ExtTable: {
+ WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled");
+ uint8_t extOp;
+ WASM_PARSER_FAIL_IF(!parseUInt8(extOp), "can't parse table extended opcode");
+ unsigned tableIndex;
+ WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't parse table index");
+
+ switch (static_cast<ExtTableOpType>(extOp)) {
+ case ExtTableOpType::TableSize: {
+ ExpressionType result;
+ WASM_TRY_ADD_TO_CONTEXT(addTableSize(tableIndex, result));
+ m_expressionStack.append(result);
+ break;
+ }
+ case ExtTableOpType::TableGrow: {
+ ExpressionType fill, delta, result;
+ WASM_TRY_POP_EXPRESSION_STACK_INTO(delta, "table.grow");
+ WASM_TRY_POP_EXPRESSION_STACK_INTO(fill, "table.grow");
+ WASM_TRY_ADD_TO_CONTEXT(addTableGrow(tableIndex, fill, delta, result));
+ m_expressionStack.append(result);
+ break;
+ }
+ case ExtTableOpType::TableFill: {
+ ExpressionType offset, fill, count;
+ WASM_TRY_POP_EXPRESSION_STACK_INTO(count, "table.fill");
+ WASM_TRY_POP_EXPRESSION_STACK_INTO(fill, "table.fill");
+ WASM_TRY_POP_EXPRESSION_STACK_INTO(offset, "table.fill");
+ WASM_TRY_ADD_TO_CONTEXT(addTableFill(tableIndex, offset, fill, count));
+ break;
+ }
+ default:
+ WASM_PARSER_FAIL_IF(true, "invalid extended table op ", extOp);
+ break;
+ }
+ return { };
+ }
+
case RefNull: {
WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled");
m_expressionStack.append(m_context.addConstant(Anyfunc, JSValue::encode(jsNull())));
@@ -688,6 +725,7 @@
return { };
}
+ case ExtTable:
case TableGet:
case TableSet: {
unsigned tableIndex;
Modified: trunk/Source/_javascript_Core/wasm/WasmInstance.cpp (246576 => 246577)
--- trunk/Source/_javascript_Core/wasm/WasmInstance.cpp 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/Source/_javascript_Core/wasm/WasmInstance.cpp 2019-06-19 01:18:43 UTC (rev 246577)
@@ -159,6 +159,40 @@
return true;
}
+int32_t doWasmTableGrow(Instance* instance, unsigned tableIndex, EncodedJSValue fill, int32_t delta)
+{
+ ASSERT(tableIndex < instance->module().moduleInformation().tableCount());
+ auto oldSize = instance->table(tableIndex)->length();
+ if (delta < 0)
+ return oldSize;
+ auto newSize = instance->table(tableIndex)->grow(delta);
+ if (!newSize || *newSize == oldSize)
+ return -1;
+
+ for (unsigned i = oldSize; i < instance->table(tableIndex)->length(); ++i)
+ setWasmTableElement(instance, tableIndex, i, fill);
+
+ return oldSize;
+}
+
+bool doWasmTableFill(Instance* instance, unsigned tableIndex, int32_t unsafeOffset, EncodedJSValue fill, int32_t unsafeCount)
+{
+ ASSERT(tableIndex < instance->module().moduleInformation().tableCount());
+ if (unsafeOffset < 0 || unsafeCount < 0)
+ return false;
+
+ unsigned offset = unsafeOffset;
+ unsigned count = unsafeCount;
+
+ if (offset >= instance->table(tableIndex)->length() || offset + count > instance->table(tableIndex)->length())
+ return false;
+
+ for (unsigned j = 0; j < count; ++j)
+ setWasmTableElement(instance, tableIndex, offset + j, fill);
+
+ return true;
+}
+
EncodedJSValue doWasmRefFunc(Instance* instance, uint32_t index)
{
JSValue value = instance->getFunctionWrapper(index);
Modified: trunk/Source/_javascript_Core/wasm/WasmInstance.h (246576 => 246577)
--- trunk/Source/_javascript_Core/wasm/WasmInstance.h 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/Source/_javascript_Core/wasm/WasmInstance.h 2019-06-19 01:18:43 UTC (rev 246577)
@@ -44,6 +44,8 @@
EncodedJSValue getWasmTableElement(Instance*, unsigned, int32_t);
bool setWasmTableElement(Instance*, unsigned, int32_t, EncodedJSValue encValue);
EncodedJSValue doWasmRefFunc(Instance*, uint32_t);
+int32_t doWasmTableGrow(Instance*, unsigned, EncodedJSValue fill, int32_t delta);
+bool doWasmTableFill(Instance*, unsigned, int32_t offset, EncodedJSValue fill, int32_t count);
class Instance : public ThreadSafeRefCounted<Instance>, public CanMakeWeakPtr<Instance> {
public:
Modified: trunk/Source/_javascript_Core/wasm/WasmValidate.cpp (246576 => 246577)
--- trunk/Source/_javascript_Core/wasm/WasmValidate.cpp 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/Source/_javascript_Core/wasm/WasmValidate.cpp 2019-06-19 01:18:43 UTC (rev 246577)
@@ -108,7 +108,9 @@
// Tables
Result WARN_UNUSED_RETURN addTableGet(unsigned, ExpressionType& index, ExpressionType& result);
Result WARN_UNUSED_RETURN addTableSet(unsigned, ExpressionType& index, ExpressionType& value);
-
+ Result WARN_UNUSED_RETURN addTableSize(unsigned, ExpressionType& result);
+ Result WARN_UNUSED_RETURN addTableGrow(unsigned, ExpressionType& fill, ExpressionType& delta, ExpressionType& result);
+ Result WARN_UNUSED_RETURN addTableFill(unsigned, ExpressionType& offset, ExpressionType& fill, ExpressionType& count);
// Locals
Result WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
Result WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
@@ -198,6 +200,34 @@
return { };
}
+auto Validate::addTableSize(unsigned tableIndex, ExpressionType& result) -> Result
+{
+ result = Type::I32;
+ WASM_VALIDATOR_FAIL_IF(tableIndex >= m_module.tableCount(), "table index ", tableIndex, " is invalid, limit is ", m_module.tableCount());
+
+ return { };
+}
+
+auto Validate::addTableGrow(unsigned tableIndex, ExpressionType& fill, ExpressionType& delta, ExpressionType& result) -> Result
+{
+ result = Type::I32;
+ WASM_VALIDATOR_FAIL_IF(tableIndex >= m_module.tableCount(), "table index ", tableIndex, " is invalid, limit is ", m_module.tableCount());
+ WASM_VALIDATOR_FAIL_IF(!isSubtype(fill, m_module.tables[tableIndex].wasmType()), "table.grow expects fill value of type ", m_module.tables[tableIndex].wasmType(), " got ", fill);
+ WASM_VALIDATOR_FAIL_IF(Type::I32 != delta, "table.grow expects an i32 delta value, got ", delta);
+
+ return { };
+}
+
+auto Validate::addTableFill(unsigned tableIndex, ExpressionType& offset, ExpressionType& fill, ExpressionType& count) -> Result
+{
+ WASM_VALIDATOR_FAIL_IF(tableIndex >= m_module.tableCount(), "table index ", tableIndex, " is invalid, limit is ", m_module.tableCount());
+ WASM_VALIDATOR_FAIL_IF(!isSubtype(fill, m_module.tables[tableIndex].wasmType()), "table.fill expects fill value of type ", m_module.tables[tableIndex].wasmType(), " got ", fill);
+ WASM_VALIDATOR_FAIL_IF(Type::I32 != offset, "table.fill expects an i32 offset value, got ", offset);
+ WASM_VALIDATOR_FAIL_IF(Type::I32 != count, "table.fill expects an i32 count value, got ", count);
+
+ return { };
+}
+
auto Validate::addRefIsNull(ExpressionType& value, ExpressionType& result) -> Result
{
result = Type::I32;
Modified: trunk/Source/_javascript_Core/wasm/generateWasmOpsHeader.py (246576 => 246577)
--- trunk/Source/_javascript_Core/wasm/generateWasmOpsHeader.py 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/Source/_javascript_Core/wasm/generateWasmOpsHeader.py 2019-06-19 01:18:43 UTC (rev 246577)
@@ -55,17 +55,17 @@
type_definitions = "".join(type_definitions)
-def opcodeMacroizer(filter):
+def opcodeMacroizer(filter, opcodeField="value"):
inc = 0
for op in wasm.opcodeIterator(filter):
b3op = "Oops"
if isSimple(op["opcode"]):
b3op = op["opcode"]["b3op"]
- yield cppMacro(op["name"], op["opcode"]["value"], b3op, inc)
+ yield cppMacro(op["name"], op["opcode"][opcodeField], b3op, inc)
inc += 1
defines = ["#define FOR_EACH_WASM_SPECIAL_OP(macro)"]
-defines.extend([op for op in opcodeMacroizer(lambda op: not (isUnary(op) or isBinary(op) or op["category"] == "control" or op["category"] == "memory"))])
+defines.extend([op for op in opcodeMacroizer(lambda op: not (isUnary(op) or isBinary(op) or op["category"] == "control" or op["category"] == "memory" or op["category"] == "exttable"))])
defines.append("\n\n#define FOR_EACH_WASM_CONTROL_FLOW_OP(macro)")
defines.extend([op for op in opcodeMacroizer(lambda op: op["category"] == "control")])
defines.append("\n\n#define FOR_EACH_WASM_SIMPLE_UNARY_OP(macro)")
@@ -80,6 +80,8 @@
defines.extend([op for op in opcodeMacroizer(lambda op: (op["category"] == "memory" and len(op["return"]) == 1))])
defines.append("\n\n#define FOR_EACH_WASM_MEMORY_STORE_OP(macro)")
defines.extend([op for op in opcodeMacroizer(lambda op: (op["category"] == "memory" and len(op["return"]) == 0))])
+defines.append("\n\n#define FOR_EACH_WASM_EXT_TABLE_OP(macro)")
+defines.extend([op for op in opcodeMacroizer(lambda op: (op["category"] == "exttable"), "extendedOp")])
defines.append("\n\n")
defines = "".join(defines)
@@ -202,7 +204,8 @@
FOR_EACH_WASM_UNARY_OP(macro) \\
FOR_EACH_WASM_BINARY_OP(macro) \\
FOR_EACH_WASM_MEMORY_LOAD_OP(macro) \\
- FOR_EACH_WASM_MEMORY_STORE_OP(macro)
+ FOR_EACH_WASM_MEMORY_STORE_OP(macro) \\
+ macro(ExtTable, 0xFC, Oops, 0)
#define CREATE_ENUM_VALUE(name, id, b3op, inc) name = id,
@@ -234,6 +237,10 @@
FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_ENUM_VALUE)
};
+enum class ExtTableOpType : uint8_t {
+ FOR_EACH_WASM_EXT_TABLE_OP(CREATE_ENUM_VALUE)
+};
+
#undef CREATE_ENUM_VALUE
inline bool isControlOp(OpType op)
Modified: trunk/Source/_javascript_Core/wasm/wasm.json (246576 => 246577)
--- trunk/Source/_javascript_Core/wasm/wasm.json 2019-06-18 23:08:22 UTC (rev 246576)
+++ trunk/Source/_javascript_Core/wasm/wasm.json 2019-06-19 01:18:43 UTC (rev 246577)
@@ -69,6 +69,9 @@
"set_global": { "category": "special", "value": 36, "return": [], "parameter": ["any"], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "write a global variable" },
"table.get": { "category": "special", "value": 37, "return": ["anyref"], "parameter": ["i32"], "immediate": [{"name": "table_index", "type": "varuint32"}], "description": "get a table value" },
"table.set": { "category": "special", "value": 38, "return": [], "parameter": ["i32", "anyref"], "immediate": [{"name": "table_index", "type": "varuint32"}], "description": "set a table value" },
+ "table.size": { "category": "exttable", "value": 252, "return": ["i32"], "parameter": [], "immediate": [{"name": "table_index", "type": "varuint32"}], "description": "get the size of a table", "extendedOp": 15 },
+ "table.grow": { "category": "exttable", "value": 252, "return": ["i32"], "parameter": ["anyref", "i32"], "immediate": [{"name": "table_index", "type": "varuint32"}], "description": "grow a table by the given delta and return the previous size, or -1 if enough space cannot be allocated", "extendedOp": 16 },
+ "table.fill": { "category": "exttable", "value": 252, "return": ["i32"], "parameter": ["i32", "anyref", "i32"], "immediate": [{"name": "table_index", "type": "varuint32"}], "description": "fill entries [i,i+n) with the given value", "extendedOp": 17 },
"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": "table_index","type": "varuint32"}],"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" },