mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-30 05:35:31 +00:00
Bug 1284155 - Baldr: allow cross-element Table elements (r=bbouvier)
MozReview-Commit-ID: 3xalhzMAeJp
This commit is contained in:
parent
555b22b818
commit
9ba6432be7
@ -1085,9 +1085,7 @@ WasmTableObject::setImpl(JSContext* cx, const CallArgs& args)
|
||||
const FuncExport& funcExport = instance.metadata().lookupFuncExport(funcIndex);
|
||||
const CodeRange& codeRange = instance.metadata().codeRanges[funcExport.codeRangeIndex()];
|
||||
void* code = instance.codeSegment().base() + codeRange.funcTableEntry();
|
||||
|
||||
if (!table.set(cx, index, code, instance))
|
||||
return false;
|
||||
table.set(index, code, instance);
|
||||
} else {
|
||||
table.setNull(index);
|
||||
}
|
||||
|
@ -467,9 +467,7 @@ Module::initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
|
||||
void* code = codeBase + seg.elems[i];
|
||||
if (useProfilingEntry)
|
||||
code = codeBase + instance.code().lookupRange(code)->funcProfilingEntry();
|
||||
|
||||
if (!table.set(cx, offset + i, code, instance))
|
||||
return false;
|
||||
table.set(offset + i, code, instance);
|
||||
}
|
||||
|
||||
prevEnd = offset + seg.elems.length();
|
||||
|
@ -99,22 +99,16 @@ Table::externalArray() const
|
||||
return (ExternalTableElem*)array_.get();
|
||||
}
|
||||
|
||||
bool
|
||||
Table::set(JSContext* cx, uint32_t index, void* code, Instance& instance)
|
||||
void
|
||||
Table::set(uint32_t index, void* code, Instance& instance)
|
||||
{
|
||||
if (external_) {
|
||||
ExternalTableElem& elem = externalArray()[index];
|
||||
if (elem.tls->instance != &instance) {
|
||||
JS_ReportError(cx, "cross-module Table import NYI");
|
||||
return false;
|
||||
}
|
||||
|
||||
elem.code = code;
|
||||
elem.tls = &instance.tlsData();
|
||||
} else {
|
||||
internalArray()[index] = code;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -58,7 +58,7 @@ class Table : public ShareableBase<Table>
|
||||
|
||||
void** internalArray() const;
|
||||
ExternalTableElem* externalArray() const;
|
||||
bool set(JSContext* cx, uint32_t index, void* code, Instance& instance);
|
||||
void set(uint32_t index, void* code, Instance& instance);
|
||||
void setNull(uint32_t index);
|
||||
|
||||
// about:memory reporting:
|
||||
|
@ -261,7 +261,7 @@ assertEq(e.tbl1.get(1), null);
|
||||
e.tbl1.set(3, e.f1);
|
||||
assertEq(e.tbl1.get(0), e.tbl1.get(3));
|
||||
|
||||
// Re-exports:
|
||||
// Re-exports and Identity:
|
||||
|
||||
var code = textToBinary('(module (import "a" "b" (memory 1 1)) (export "foo" memory) (export "bar" memory))');
|
||||
var mem = new Memory({initial:1});
|
||||
@ -275,6 +275,18 @@ var e = new Instance(new Module(code), {a:{b:tbl}}).exports;
|
||||
assertEq(tbl, e.foo);
|
||||
assertEq(tbl, e.bar);
|
||||
|
||||
var code = textToBinary('(module (import "a" "b" (table 2 2)) (func $foo) (elem (i32.const 0) $foo) (export "foo" $foo))');
|
||||
var tbl = new Table({initial:2, element:"anyfunc"});
|
||||
var e1 = new Instance(new Module(code), {a:{b:tbl}}).exports;
|
||||
assertEq(e1.foo, tbl.get(0));
|
||||
tbl.set(1, e1.foo);
|
||||
assertEq(e1.foo, tbl.get(1));
|
||||
var e2 = new Instance(new Module(code), {a:{b:tbl}}).exports;
|
||||
assertEq(e2.foo, tbl.get(0));
|
||||
assertEq(e1.foo, tbl.get(1));
|
||||
assertEq(tbl.get(0) === e1.foo, false);
|
||||
assertEq(e1.foo === e2.foo, false);
|
||||
|
||||
// Non-existent export errors
|
||||
|
||||
assertErrorMessage(() => new Module(textToBinary('(module (export "a" 0))')), TypeError, /exported function index out of bounds/);
|
||||
|
@ -117,10 +117,10 @@ Error);
|
||||
(func $foo (result i32) (i32.const 42))
|
||||
(export "foo" $foo)
|
||||
(func $bar (result i32) (i32.const 13))
|
||||
(table $foo $bar)
|
||||
(table (resizable 10))
|
||||
(elem (i32.const 0) $foo $bar)
|
||||
(export "tbl" table)
|
||||
)
|
||||
`))).exports;
|
||||
)`))).exports;
|
||||
assertEq(e.foo(), 42);
|
||||
assertEq(e.tbl.get(0)(), 42);
|
||||
assertEq(e.tbl.get(1)(), 13);
|
||||
@ -151,6 +151,35 @@ Error);
|
||||
assertEq(e.tbl.get(1)(), 13);
|
||||
assertEqStacks(disableSingleStepProfiling(), ["", ">", "0,>", ">", "", ">", "1,>", ">", ""]);
|
||||
disableSPSProfiling();
|
||||
|
||||
var e2 = new Instance(new Module(textToBinary(`
|
||||
(module
|
||||
(type $v2i (func (result i32)))
|
||||
(import "a" "b" (table 10))
|
||||
(elem (i32.const 2) $bar)
|
||||
(func $bar (result i32) (i32.const 99))
|
||||
(func $baz (param $i i32) (result i32) (call_indirect $v2i (get_local $i)))
|
||||
(export "baz" $baz)
|
||||
)`)),
|
||||
{a:{b:e.tbl}}).exports;
|
||||
|
||||
enableSPSProfiling();
|
||||
enableSingleStepProfiling();
|
||||
assertEq(e2.baz(0), 42);
|
||||
assertEqStacks(disableSingleStepProfiling(), ["", ">", "1,>", "0,1,>", "1,>", ">", ""]);
|
||||
disableSPSProfiling();
|
||||
|
||||
enableSPSProfiling();
|
||||
enableSingleStepProfiling();
|
||||
assertEq(e2.baz(1), 13);
|
||||
assertEqStacks(disableSingleStepProfiling(), ["", ">", "1,>", "1,1,>", "1,>", ">", ""]);
|
||||
disableSPSProfiling();
|
||||
|
||||
enableSPSProfiling();
|
||||
enableSingleStepProfiling();
|
||||
assertEq(e2.baz(2), 99);
|
||||
assertEqStacks(disableSingleStepProfiling(), ["", ">", "1,>", "0,1,>", "1,>", ">", ""]);
|
||||
disableSPSProfiling();
|
||||
})();
|
||||
|
||||
(function() {
|
||||
|
@ -152,3 +152,79 @@ assertEq(finalizeCount(), 2);
|
||||
t = null;
|
||||
gc();
|
||||
assertEq(finalizeCount(), 4);
|
||||
|
||||
// Once all of an instance's elements in a Table (including the
|
||||
// bad-indirect-call stub) have been clobbered, the Instance should not be
|
||||
// rooted.
|
||||
resetFinalizeCount();
|
||||
var i1 = evalText(`(module (func $f1 (result i32) (i32.const 13)) (export "f1" $f1))`);
|
||||
var i2 = evalText(`(module (func $f2 (result i32) (i32.const 42)) (export "f2" $f2))`);
|
||||
var f1 = i1.exports.f1;
|
||||
var f2 = i2.exports.f2;
|
||||
var t = new Table({initial:2, element:"anyfunc"});
|
||||
i1.edge = makeFinalizeObserver();
|
||||
i2.edge = makeFinalizeObserver();
|
||||
f1.edge = makeFinalizeObserver();
|
||||
f2.edge = makeFinalizeObserver();
|
||||
t.edge = makeFinalizeObserver();
|
||||
t.set(0, f1);
|
||||
t.set(1, f2);
|
||||
gc();
|
||||
assertEq(finalizeCount(), 0);
|
||||
f1 = f2 = null;
|
||||
i1.exports = null;
|
||||
i2.exports = null;
|
||||
gc();
|
||||
assertEq(finalizeCount(), 2);
|
||||
i1 = null;
|
||||
i2 = null;
|
||||
gc();
|
||||
assertEq(finalizeCount(), 2);
|
||||
t.set(0, t.get(1));
|
||||
gc();
|
||||
assertEq(finalizeCount(), 3);
|
||||
t = null;
|
||||
gc();
|
||||
assertEq(finalizeCount(), 5);
|
||||
|
||||
// Ensure that an instance that is only live on the stack cannot be GC even if
|
||||
// there are no outstanding references.
|
||||
resetFinalizeCount();
|
||||
const N = 10;
|
||||
var tbl = new Table({initial:N, element:"anyfunc"});
|
||||
tbl.edge = makeFinalizeObserver();
|
||||
function runTest() {
|
||||
tbl = null;
|
||||
gc();
|
||||
assertEq(finalizeCount(), 1);
|
||||
return 100;
|
||||
}
|
||||
var i = evalText(
|
||||
`(module
|
||||
(import "a" "b" (result i32))
|
||||
(func $f (param i32) (result i32) (call_import 0))
|
||||
(export "f" $f)
|
||||
)`,
|
||||
{a:{b:runTest}}
|
||||
);
|
||||
i.edge = makeFinalizeObserver();
|
||||
tbl.set(0, i.exports.f);
|
||||
var m = new Module(textToBinary(`(module
|
||||
(import "a" "b" (table ${N}))
|
||||
(type $i2i (func (param i32) (result i32)))
|
||||
(func $f (param $i i32) (result i32)
|
||||
(set_local $i (i32.sub (get_local $i) (i32.const 1)))
|
||||
(i32.add
|
||||
(i32.const 1)
|
||||
(call_indirect $i2i (get_local $i) (get_local $i))))
|
||||
(export "f" $f)
|
||||
)`));
|
||||
for (var i = 1; i < N; i++) {
|
||||
var inst = new Instance(m, {a:{b:tbl}});
|
||||
inst.edge = makeFinalizeObserver();
|
||||
tbl.set(i, inst.exports.f);
|
||||
}
|
||||
inst = null;
|
||||
assertEq(tbl.get(N - 1)(N - 1), 109);
|
||||
gc();
|
||||
assertEq(finalizeCount(), N + 1);
|
||||
|
@ -5,6 +5,7 @@ load(libdir + 'asserts.js');
|
||||
const Module = WebAssembly.Module;
|
||||
const Instance = WebAssembly.Instance;
|
||||
const Table = WebAssembly.Table;
|
||||
const Memory = WebAssembly.Memory;
|
||||
|
||||
// Explicitly opt into the new binary format for imports and exports until it
|
||||
// is used by default everywhere.
|
||||
@ -65,11 +66,90 @@ assertErrorMessage(() => call(6), Error, /bad wasm indirect call/);
|
||||
assertErrorMessage(() => call(10), Error, /out-of-range/);
|
||||
|
||||
var tbl = new Table({initial:3, element:"anyfunc"});
|
||||
var call = evalText(`(module (import "a" "b" (table 2)) (export "tbl" table) (elem (i32.const 0) $f0 $f1) ${callee(0)} ${callee(1)} ${caller})`, {a:{b:tbl}}).exports.call;
|
||||
var call = evalText(`(module (import "a" "b" (table 3)) (export "tbl" table) (elem (i32.const 0) $f0 $f1) ${callee(0)} ${callee(1)} ${caller})`, {a:{b:tbl}}).exports.call;
|
||||
assertEq(call(0), 0);
|
||||
assertEq(call(1), 1);
|
||||
assertEq(tbl.get(0)(), 0);
|
||||
assertEq(tbl.get(1)(), 1);
|
||||
assertErrorMessage(() => call(2), Error, /bad wasm indirect call/);
|
||||
assertEq(tbl.get(2), null);
|
||||
|
||||
var exp = evalText(`(module (import "a" "b" (table 3)) (export "tbl" table) (elem (i32.const 2) $f2) ${callee(2)} ${caller})`, {a:{b:tbl}}).exports;
|
||||
assertEq(exp.tbl, tbl);
|
||||
assertEq(exp.call(0), 0);
|
||||
assertEq(exp.call(1), 1);
|
||||
assertEq(exp.call(2), 2);
|
||||
assertEq(call(0), 0);
|
||||
assertEq(call(1), 1);
|
||||
assertEq(call(2), 2);
|
||||
assertEq(tbl.get(0)(), 0);
|
||||
assertEq(tbl.get(1)(), 1);
|
||||
assertEq(tbl.get(2)(), 2);
|
||||
|
||||
var exp1 = evalText(`(module (table (resizable 10)) (export "tbl" table) (elem (i32.const 0) $f0 $f0) ${callee(0)} (export "f0" $f0) ${caller})`).exports
|
||||
assertEq(exp1.tbl.get(0), exp1.f0);
|
||||
assertEq(exp1.tbl.get(1), exp1.f0);
|
||||
assertEq(exp1.tbl.get(2), null);
|
||||
assertEq(exp1.call(0), 0);
|
||||
assertEq(exp1.call(1), 0);
|
||||
assertErrorMessage(() => exp1.call(2), Error, /bad wasm indirect call/);
|
||||
var exp2 = evalText(`(module (import "a" "b" (table 10)) (export "tbl" table) (elem (i32.const 1) $f1 $f1) ${callee(1)} (export "f1" $f1) ${caller})`, {a:{b:exp1.tbl}}).exports
|
||||
assertEq(exp1.tbl, exp2.tbl);
|
||||
assertEq(exp2.tbl.get(0), exp1.f0);
|
||||
assertEq(exp2.tbl.get(1), exp2.f1);
|
||||
assertEq(exp2.tbl.get(2), exp2.f1);
|
||||
assertEq(exp1.call(0), 0);
|
||||
assertEq(exp1.call(1), 1);
|
||||
assertEq(exp1.call(2), 1);
|
||||
assertEq(exp2.call(0), 0);
|
||||
assertEq(exp2.call(1), 1);
|
||||
assertEq(exp2.call(2), 1);
|
||||
|
||||
var tbl = new Table({initial:3, element:"anyfunc"});
|
||||
var e1 = evalText(`(module (func $f (result i32) (i32.const 42)) (export "f" $f))`).exports;
|
||||
var e2 = evalText(`(module (func $g (result f32) (f32.const 10)) (export "g" $g))`).exports;
|
||||
var e3 = evalText(`(module (func $h (result i32) (i32.const 13)) (export "h" $h))`).exports;
|
||||
tbl.set(0, e1.f);
|
||||
tbl.set(1, e2.g);
|
||||
tbl.set(2, e3.h);
|
||||
var e4 = evalText(`(module (import "a" "b" (table 3)) ${caller})`, {a:{b:tbl}}).exports;
|
||||
assertEq(e4.call(0), 42);
|
||||
assertErrorMessage(() => e4.call(1), Error, /bad wasm indirect call/);
|
||||
assertEq(e4.call(2), 13);
|
||||
|
||||
var m = new Module(textToBinary(`(module
|
||||
(type $i2i (func (param i32) (result i32)))
|
||||
(import "a" "mem" (memory 1))
|
||||
(import "a" "tbl" (table 10))
|
||||
(import $imp "a" "imp" (result i32))
|
||||
(func $call (param $i i32) (result i32)
|
||||
(i32.add
|
||||
(call_import $imp)
|
||||
(i32.add
|
||||
(i32.load (i32.const 0))
|
||||
(if (i32.eqz (get_local $i))
|
||||
(then (i32.const 0))
|
||||
(else
|
||||
(set_local $i (i32.sub (get_local $i) (i32.const 1)))
|
||||
(call_indirect $i2i (get_local $i) (get_local $i)))))))
|
||||
(export "call" $call)
|
||||
)`));
|
||||
var failTime = false;
|
||||
var tbl = new Table({initial:10, element:"anyfunc"});
|
||||
var mem1 = new Memory({initial:1});
|
||||
var e1 = new Instance(m, {a:{mem:mem1, tbl, imp() {if (failTime) throw new Error("ohai"); return 1}}}).exports;
|
||||
tbl.set(0, e1.call);
|
||||
var mem2 = new Memory({initial:1});
|
||||
var e2 = new Instance(m, {a:{mem:mem2, tbl, imp() {return 10} }}).exports;
|
||||
tbl.set(1, e2.call);
|
||||
var mem3 = new Memory({initial:1});
|
||||
var e3 = new Instance(m, {a:{mem:mem3, tbl, imp() {return 100} }}).exports;
|
||||
new Int32Array(mem1.buffer)[0] = 1000;
|
||||
new Int32Array(mem2.buffer)[0] = 10000;
|
||||
new Int32Array(mem3.buffer)[0] = 100000;
|
||||
assertEq(e3.call(2), 111111);
|
||||
failTime = true;
|
||||
assertErrorMessage(() => e3.call(2), Error, "ohai");
|
||||
|
||||
// Call signatures are matched structurally:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user