зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1620197 - Enable multiple results from WebAssembly functions r=lth
This patch enables multi-value calls and returns, adding some tests, and conditionally disabling a couple expect-fail reftests that now pass. It also changes to make calling multi-result WebAssembly functions from JavaScript raise a run-time error, and likewise for calling out to multi-result JS function from wasm. Previously these would abort at stub generation time. Differential Revision: https://phabricator.services.mozilla.com/D65502 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
23f67ba1d1
Коммит
b448754196
|
@ -0,0 +1,23 @@
|
||||||
|
|
||||||
|
function expectRunFailure(text, pattern, imports) {
|
||||||
|
let instance = wasmEvalText(text, imports);
|
||||||
|
assertErrorMessage(() => instance.exports.run(),
|
||||||
|
TypeError,
|
||||||
|
pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
expectRunFailure(`
|
||||||
|
(module
|
||||||
|
(import "env" "f" (func $f (result i32 i32)))
|
||||||
|
(func (export "run") (result i32)
|
||||||
|
(call $f)
|
||||||
|
i32.sub))`,
|
||||||
|
/calling JavaScript functions with multiple results from WebAssembly not yet implemented/,
|
||||||
|
{ env: { f: () => [52, 10] } });
|
||||||
|
|
||||||
|
expectRunFailure(`
|
||||||
|
(module
|
||||||
|
(func (export "run") (result i32 i32)
|
||||||
|
(i32.const 52)
|
||||||
|
(i32.const 10)))`,
|
||||||
|
/calling WebAssembly functions with multiple results from JavaScript not yet implemented/);
|
|
@ -0,0 +1,31 @@
|
||||||
|
// |jit-test| skip-if: !wasmReftypesEnabled()
|
||||||
|
|
||||||
|
let counter = 0;
|
||||||
|
let i = new WebAssembly.Instance(
|
||||||
|
new WebAssembly.Module(wasmTextToBinary(`
|
||||||
|
(module
|
||||||
|
(func $boxNextInt (import "imports" "boxNextInt")
|
||||||
|
(result anyref))
|
||||||
|
(func $unboxInt (import "imports" "unboxInt")
|
||||||
|
(param anyref) (result i32))
|
||||||
|
|
||||||
|
(func $boxNextTwoInts (result anyref anyref)
|
||||||
|
call $boxNextInt
|
||||||
|
call $boxNextInt)
|
||||||
|
|
||||||
|
(func $unboxTwoInts (param anyref anyref) (result i32 i32)
|
||||||
|
local.get 0
|
||||||
|
call $unboxInt
|
||||||
|
local.get 1
|
||||||
|
call $unboxInt)
|
||||||
|
|
||||||
|
(func $addNextTwoInts (export "addNextTwoInts") (result i32)
|
||||||
|
call $boxNextTwoInts
|
||||||
|
call $unboxTwoInts
|
||||||
|
i32.add))`)),
|
||||||
|
{imports: {boxNextInt: () => ({val: counter++}),
|
||||||
|
unboxInt: box => box.val}});
|
||||||
|
|
||||||
|
for (let n = 0; n < 200000; n += 2) {
|
||||||
|
assertEq(i.exports.addNextTwoInts(), n * 2 + 1);
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
wasmFullPass(`
|
||||||
|
(module
|
||||||
|
(func (result i32 i32)
|
||||||
|
(i32.const 52)
|
||||||
|
(i32.const 10))
|
||||||
|
(func (export "run") (result i32)
|
||||||
|
(call 0)
|
||||||
|
i32.sub))`,
|
||||||
|
42);
|
||||||
|
|
||||||
|
wasmFullPass(`
|
||||||
|
(module
|
||||||
|
(func (param i32 i32) (result i32 i32)
|
||||||
|
(local.get 0)
|
||||||
|
(local.get 1))
|
||||||
|
(func (export "run") (result i32)
|
||||||
|
(i32.const 52)
|
||||||
|
(i32.const 10)
|
||||||
|
(call 0)
|
||||||
|
i32.sub))`,
|
||||||
|
42);
|
||||||
|
|
||||||
|
wasmFullPass(`
|
||||||
|
(module
|
||||||
|
(func (param i32 i32 i32 i32 i32
|
||||||
|
i32 i32 i32 i32 i32)
|
||||||
|
(result i32 i32)
|
||||||
|
(local.get 8)
|
||||||
|
(local.get 9))
|
||||||
|
(func (export "run") (result i32)
|
||||||
|
(i32.const 0)
|
||||||
|
(i32.const 1)
|
||||||
|
(i32.const 2)
|
||||||
|
(i32.const 3)
|
||||||
|
(i32.const 4)
|
||||||
|
(i32.const 5)
|
||||||
|
(i32.const 6)
|
||||||
|
(i32.const 7)
|
||||||
|
(i32.const 52)
|
||||||
|
(i32.const 10)
|
||||||
|
(call 0)
|
||||||
|
i32.sub))`,
|
||||||
|
42);
|
||||||
|
|
||||||
|
wasmFullPass(`
|
||||||
|
(module
|
||||||
|
(func (param i32 i32 i32 i32 i32
|
||||||
|
i32 i32 i32 i32 i32)
|
||||||
|
(result i32 i32)
|
||||||
|
(local i32 i32 i32 i32)
|
||||||
|
(local.get 8)
|
||||||
|
(local.get 9))
|
||||||
|
(func (export "run") (result i32)
|
||||||
|
(i32.const 0)
|
||||||
|
(i32.const 1)
|
||||||
|
(i32.const 2)
|
||||||
|
(i32.const 3)
|
||||||
|
(i32.const 4)
|
||||||
|
(i32.const 5)
|
||||||
|
(i32.const 6)
|
||||||
|
(i32.const 7)
|
||||||
|
(i32.const 52)
|
||||||
|
(i32.const 10)
|
||||||
|
(call 0)
|
||||||
|
i32.sub))`,
|
||||||
|
42);
|
||||||
|
|
||||||
|
wasmFullPass(`
|
||||||
|
(module
|
||||||
|
(func (param i32 i32 i32 i32 i32
|
||||||
|
i32 i32 i32 i32 i32)
|
||||||
|
(result i32 i32 i32)
|
||||||
|
(local i32 i32 i32 i32)
|
||||||
|
(local.get 7)
|
||||||
|
(local.get 8)
|
||||||
|
(local.get 9))
|
||||||
|
(func (export "run") (result i32)
|
||||||
|
(i32.const 0)
|
||||||
|
(i32.const 1)
|
||||||
|
(i32.const 2)
|
||||||
|
(i32.const 3)
|
||||||
|
(i32.const 4)
|
||||||
|
(i32.const 5)
|
||||||
|
(i32.const 6)
|
||||||
|
(i32.const 7)
|
||||||
|
(i32.const 52)
|
||||||
|
(i32.const 10)
|
||||||
|
(call 0)
|
||||||
|
i32.sub
|
||||||
|
i32.sub))`,
|
||||||
|
-35);
|
||||||
|
|
||||||
|
wasmFullPass(`
|
||||||
|
(module
|
||||||
|
(func (param i32 i64 i32 i64 i32
|
||||||
|
i64 i32 i64 i32 i64)
|
||||||
|
(result i64 i32 i64)
|
||||||
|
(local i32 i64 i32 i64)
|
||||||
|
(local.get 7)
|
||||||
|
(local.get 8)
|
||||||
|
(local.get 9))
|
||||||
|
(func (export "run") (result i32)
|
||||||
|
(i32.const 0)
|
||||||
|
(i64.const 1)
|
||||||
|
(i32.const 2)
|
||||||
|
(i64.const 3)
|
||||||
|
(i32.const 4)
|
||||||
|
(i64.const 5)
|
||||||
|
(i32.const 6)
|
||||||
|
(i64.const 7)
|
||||||
|
(i32.const 52)
|
||||||
|
(i64.const 10)
|
||||||
|
(call 0)
|
||||||
|
i32.wrap_i64
|
||||||
|
i32.sub
|
||||||
|
i64.extend_i32_s
|
||||||
|
i64.sub
|
||||||
|
i32.wrap_i64))`,
|
||||||
|
-35);
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
wasmValidateText(`
|
||||||
|
(module
|
||||||
|
(func (result i32 i32)
|
||||||
|
(i32.const 32)
|
||||||
|
(i32.const 10)))`);
|
||||||
|
|
||||||
|
wasmValidateText(`
|
||||||
|
(module
|
||||||
|
(type $t (func (result i32 i32)))
|
||||||
|
(func (type $t)
|
||||||
|
(i32.const 32)
|
||||||
|
(i32.const 10)))`);
|
||||||
|
|
||||||
|
wasmValidateText(`
|
||||||
|
(module
|
||||||
|
(func (result i32 i32)
|
||||||
|
(block (result i32 i32)
|
||||||
|
(i32.const 32)
|
||||||
|
(i32.const 10))))`);
|
||||||
|
|
||||||
|
wasmValidateText(`
|
||||||
|
(module
|
||||||
|
(func $return-2 (result i32 i32)
|
||||||
|
(i32.const 32)
|
||||||
|
(i32.const 10))
|
||||||
|
(func $tail-call (result i32 i32)
|
||||||
|
(call 0)))`);
|
||||||
|
|
||||||
|
wasmValidateText(`
|
||||||
|
(module
|
||||||
|
(func $return-2 (result i32 i32)
|
||||||
|
(i32.const 32)
|
||||||
|
(i32.const 10))
|
||||||
|
(func $add (result i32)
|
||||||
|
(call 0)
|
||||||
|
i32.add))`);
|
||||||
|
|
||||||
|
wasmValidateText(`
|
||||||
|
(module
|
||||||
|
(func $return-2 (param i32 i32) (result i32 i32)
|
||||||
|
(local.get 0)
|
||||||
|
(local.get 1))
|
||||||
|
(func (export "run") (result i32)
|
||||||
|
(i32.const 32)
|
||||||
|
(i32.const 10)
|
||||||
|
(call 0)
|
||||||
|
i32.add))`);
|
|
@ -1186,7 +1186,7 @@ void BacktrackingAllocator::allocateStackDefinition(VirtualRegister& reg) {
|
||||||
const LUse* use = ins->getOperand(0)->toUse();
|
const LUse* use = ins->getOperand(0)->toUse();
|
||||||
VirtualRegister& area = vregs[use->virtualRegister()];
|
VirtualRegister& area = vregs[use->virtualRegister()];
|
||||||
const LStackArea* areaAlloc = area.def()->output()->toStackArea();
|
const LStackArea* areaAlloc = area.def()->output()->toStackArea();
|
||||||
reg.def()->setOutput(areaAlloc->resultAlloc(ins));
|
reg.def()->setOutput(areaAlloc->resultAlloc(ins, reg.def()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ class LStackArea;
|
||||||
class LArgument;
|
class LArgument;
|
||||||
class LConstantIndex;
|
class LConstantIndex;
|
||||||
class LInstruction;
|
class LInstruction;
|
||||||
|
class LDefinition;
|
||||||
class MBasicBlock;
|
class MBasicBlock;
|
||||||
class MIRGenerator;
|
class MIRGenerator;
|
||||||
|
|
||||||
|
@ -408,7 +409,7 @@ class LStackArea : public LAllocation {
|
||||||
|
|
||||||
ResultIterator results() const { return ResultIterator(*this); }
|
ResultIterator results() const { return ResultIterator(*this); }
|
||||||
|
|
||||||
inline LStackSlot resultAlloc(LInstruction* lir) const;
|
inline LStackSlot resultAlloc(LInstruction* lir, LDefinition* def) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Arguments are reverse indices into the stack. The indices are byte indices.
|
// Arguments are reverse indices into the stack. The indices are byte indices.
|
||||||
|
|
|
@ -4445,6 +4445,7 @@ void LIRGenerator::visitWasmStackArg(MWasmStackArg* ins) {
|
||||||
void LIRGenerator::visitWasmRegisterResult(MWasmRegisterResult* ins) {
|
void LIRGenerator::visitWasmRegisterResult(MWasmRegisterResult* ins) {
|
||||||
auto* lir = new (alloc()) LWasmRegisterResult();
|
auto* lir = new (alloc()) LWasmRegisterResult();
|
||||||
uint32_t vreg = getVirtualRegister();
|
uint32_t vreg = getVirtualRegister();
|
||||||
|
MOZ_ASSERT(ins->type() != MIRType::Int64);
|
||||||
auto type = LDefinition::TypeFrom(ins->type());
|
auto type = LDefinition::TypeFrom(ins->type());
|
||||||
lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ins->loc())));
|
lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ins->loc())));
|
||||||
ins->setVirtualRegister(vreg);
|
ins->setVirtualRegister(vreg);
|
||||||
|
|
|
@ -6816,6 +6816,13 @@ inline bool LStackArea::ResultIterator::isGcPointer() const {
|
||||||
MOZ_ASSERT(!done());
|
MOZ_ASSERT(!done());
|
||||||
MWasmStackResultArea* area = alloc_.ins()->toWasmStackResultArea()->mir();
|
MWasmStackResultArea* area = alloc_.ins()->toWasmStackResultArea()->mir();
|
||||||
MIRType type = area->result(idx_).type();
|
MIRType type = area->result(idx_).type();
|
||||||
|
#ifndef JS_PUNBOX64
|
||||||
|
// LDefinition::TypeFrom isn't defined for MIRType::Int64 values on
|
||||||
|
// this platform, so here we have a special case.
|
||||||
|
if (type == MIRType::Int64) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return LDefinition::TypeFrom(type) == LDefinition::OBJECT;
|
return LDefinition::TypeFrom(type) == LDefinition::OBJECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6838,15 +6845,28 @@ class LWasmStackResult64 : public LInstructionHelper<INT64_PIECES, 1, 0> {
|
||||||
LWasmStackResult64() : LInstructionHelper(classOpcode) {}
|
LWasmStackResult64() : LInstructionHelper(classOpcode) {}
|
||||||
|
|
||||||
MWasmStackResult* mir() const { return mir_->toWasmStackResult(); }
|
MWasmStackResult* mir() const { return mir_->toWasmStackResult(); }
|
||||||
LStackSlot result(uint32_t base) const {
|
LStackSlot result(uint32_t base, LDefinition* def) {
|
||||||
return LStackSlot(base - mir()->result().offset());
|
uint32_t offset = base - mir()->result().offset();
|
||||||
|
#if defined(JS_NUNBOX32)
|
||||||
|
if (def == getDef(INT64LOW_INDEX)) {
|
||||||
|
offset -= INT64LOW_OFFSET;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(def == getDef(INT64HIGH_INDEX));
|
||||||
|
offset -= INT64HIGH_OFFSET;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
MOZ_ASSERT(def == getDef(0));
|
||||||
|
#endif
|
||||||
|
return LStackSlot(offset);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline LStackSlot LStackArea::resultAlloc(LInstruction* lir) const {
|
inline LStackSlot LStackArea::resultAlloc(LInstruction* lir,
|
||||||
|
LDefinition* def) const {
|
||||||
if (lir->isWasmStackResult64()) {
|
if (lir->isWasmStackResult64()) {
|
||||||
return lir->toWasmStackResult64()->result(base());
|
return lir->toWasmStackResult64()->result(base(), def);
|
||||||
}
|
}
|
||||||
|
MOZ_ASSERT(def == lir->getDef(0));
|
||||||
return lir->toWasmStackResult()->result(base());
|
return lir->toWasmStackResult()->result(base());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -429,6 +429,8 @@ MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {
|
||||||
MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM, 0, JSEXN_TYPEERR, "'shared' is true but maximum is not specified")
|
MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM, 0, JSEXN_TYPEERR, "'shared' is true but maximum is not specified")
|
||||||
MSG_DEF(JSMSG_WASM_GLOBAL_IMMUTABLE, 0, JSEXN_TYPEERR, "can't set value of immutable global")
|
MSG_DEF(JSMSG_WASM_GLOBAL_IMMUTABLE, 0, JSEXN_TYPEERR, "can't set value of immutable global")
|
||||||
MSG_DEF(JSMSG_WASM_NULL_REQUIRED, 0, JSEXN_TYPEERR, "nullref requires a null value")
|
MSG_DEF(JSMSG_WASM_NULL_REQUIRED, 0, JSEXN_TYPEERR, "nullref requires a null value")
|
||||||
|
MSG_DEF(JSMSG_WASM_MULTIPLE_RESULT_EXIT_UNIMPLEMENTED, 0, JSEXN_TYPEERR, "calling JavaScript functions with multiple results from WebAssembly not yet implemented")
|
||||||
|
MSG_DEF(JSMSG_WASM_MULTIPLE_RESULT_ENTRY_UNIMPLEMENTED, 0, JSEXN_TYPEERR, "calling WebAssembly functions with multiple results from JavaScript not yet implemented")
|
||||||
|
|
||||||
// Proxy
|
// Proxy
|
||||||
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
|
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
|
||||||
|
|
|
@ -1738,7 +1738,7 @@ class BaseStackFrame final : public BaseStackFrameAllocator {
|
||||||
RegPtr dest) {
|
RegPtr dest) {
|
||||||
MOZ_ASSERT(results.height() <= masm.framePushed());
|
MOZ_ASSERT(results.height() <= masm.framePushed());
|
||||||
uint32_t offsetFromSP = masm.framePushed() - results.height();
|
uint32_t offsetFromSP = masm.framePushed() - results.height();
|
||||||
masm.movePtr(AsRegister(sp_), dest);
|
masm.moveStackPtrTo(dest);
|
||||||
if (offsetFromSP) {
|
if (offsetFromSP) {
|
||||||
masm.addPtr(Imm32(offsetFromSP), dest);
|
masm.addPtr(Imm32(offsetFromSP), dest);
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,6 +239,7 @@ class FuncExport {
|
||||||
|
|
||||||
bool canHaveJitEntry() const {
|
bool canHaveJitEntry() const {
|
||||||
return !funcType_.temporarilyUnsupportedReftypeForEntry() &&
|
return !funcType_.temporarilyUnsupportedReftypeForEntry() &&
|
||||||
|
!funcType_.temporarilyUnsupportedResultCountForEntry() &&
|
||||||
JitOptions.enableWasmJitEntry;
|
JitOptions.enableWasmJitEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -603,11 +603,6 @@ static const unsigned MaxBrTableElems = 1000000;
|
||||||
static const unsigned MaxMemoryInitialPages = 16384;
|
static const unsigned MaxMemoryInitialPages = 16384;
|
||||||
static const unsigned MaxCodeSectionBytes = MaxModuleBytes;
|
static const unsigned MaxCodeSectionBytes = MaxModuleBytes;
|
||||||
|
|
||||||
// FIXME: Temporary limit to function result counts. Replace with MaxResults:
|
|
||||||
// bug 1585909.
|
|
||||||
|
|
||||||
static const unsigned MaxFuncResults = 1;
|
|
||||||
|
|
||||||
// A magic value of the FramePointer to indicate after a return to the entry
|
// A magic value of the FramePointer to indicate after a return to the entry
|
||||||
// stub that an exception has been caught and that we should throw.
|
// stub that an exception has been caught and that we should throw.
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,12 @@ bool Instance::callImport(JSContext* cx, uint32_t funcImportIndex,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fi.funcType().temporarilyUnsupportedResultCountForExit()) {
|
||||||
|
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
|
||||||
|
JSMSG_WASM_MULTIPLE_RESULT_EXIT_UNIMPLEMENTED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(fi.funcType().args().length() == argc);
|
MOZ_ASSERT(fi.funcType().args().length() == argc);
|
||||||
for (size_t i = 0; i < argc; i++) {
|
for (size_t i = 0; i < argc; i++) {
|
||||||
switch (fi.funcType().args()[i].kind()) {
|
switch (fi.funcType().args()[i].kind()) {
|
||||||
|
@ -1698,6 +1704,12 @@ bool Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (funcType->temporarilyUnsupportedResultCountForEntry()) {
|
||||||
|
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
|
||||||
|
JSMSG_WASM_MULTIPLE_RESULT_ENTRY_UNIMPLEMENTED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// The calling convention for an external call into wasm is to pass an
|
// The calling convention for an external call into wasm is to pass an
|
||||||
// array of 16-byte values where each value contains either a coerced int32
|
// array of 16-byte values where each value contains either a coerced int32
|
||||||
// (in the low word), or a double value (in the low dword) value, with the
|
// (in the low word), or a double value (in the low dword) value, with the
|
||||||
|
|
|
@ -2061,12 +2061,6 @@ inline bool OpIter<Policy>::readCallIndirect(uint32_t* funcTypeIndex,
|
||||||
|
|
||||||
const FuncType& funcType = env_.types[*funcTypeIndex].funcType();
|
const FuncType& funcType = env_.types[*funcTypeIndex].funcType();
|
||||||
|
|
||||||
// FIXME: Remove this check when full multi-value function returns land.
|
|
||||||
// Bug 1585909.
|
|
||||||
if (funcType.results().length() > MaxFuncResults) {
|
|
||||||
return fail("too many returns in signature");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef WASM_PRIVATE_REFTYPES
|
#ifdef WASM_PRIVATE_REFTYPES
|
||||||
if (env_.tables[*tableIndex].importedOrExported &&
|
if (env_.tables[*tableIndex].importedOrExported &&
|
||||||
funcType.exposesTypeIndex()) {
|
funcType.exposesTypeIndex()) {
|
||||||
|
@ -2131,12 +2125,6 @@ inline bool OpIter<Policy>::readOldCallIndirect(uint32_t* funcTypeIndex,
|
||||||
|
|
||||||
const FuncType& funcType = env_.types[*funcTypeIndex].funcType();
|
const FuncType& funcType = env_.types[*funcTypeIndex].funcType();
|
||||||
|
|
||||||
// FIXME: Remove this check when full multi-value function returns land.
|
|
||||||
// Bug 1585909.
|
|
||||||
if (funcType.results().length() > MaxFuncResults) {
|
|
||||||
return fail("too many returns in signature");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!popCallArgs(funcType.args(), argValues)) {
|
if (!popCallArgs(funcType.args(), argValues)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -641,6 +641,12 @@ static bool GenerateInterpEntry(MacroAssembler& masm, const FuncExport& fe,
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (fe.funcType().temporarilyUnsupportedResultCountForEntry()) {
|
||||||
|
// Unreachable as the Instance::callExport doesn't let us get here.
|
||||||
|
masm.breakpoint();
|
||||||
|
return FinishOffsets(masm, offsets);
|
||||||
|
}
|
||||||
|
|
||||||
// Save all caller non-volatile registers before we clobber them here and in
|
// Save all caller non-volatile registers before we clobber them here and in
|
||||||
// the wasm callee (which does not preserve non-volatile registers).
|
// the wasm callee (which does not preserve non-volatile registers).
|
||||||
masm.setFramePushed(0);
|
masm.setFramePushed(0);
|
||||||
|
@ -1566,7 +1572,8 @@ static void StackCopy(MacroAssembler& masm, MIRType type, Register scratch,
|
||||||
GenPrintIsize(DebugChannel::Import, masm, scratch);
|
GenPrintIsize(DebugChannel::Import, masm, scratch);
|
||||||
masm.store64(scratch64, dst);
|
masm.store64(scratch64, dst);
|
||||||
#endif
|
#endif
|
||||||
} else if (type == MIRType::RefOrNull || type == MIRType::Pointer) {
|
} else if (type == MIRType::RefOrNull || type == MIRType::Pointer ||
|
||||||
|
type == MIRType::StackResults) {
|
||||||
masm.loadPtr(src, scratch);
|
masm.loadPtr(src, scratch);
|
||||||
GenPrintPtr(DebugChannel::Import, masm, scratch);
|
GenPrintPtr(DebugChannel::Import, masm, scratch);
|
||||||
masm.storePtr(scratch, dst);
|
masm.storePtr(scratch, dst);
|
||||||
|
@ -1606,13 +1613,11 @@ static void FillArgumentArrayForExit(
|
||||||
|
|
||||||
ArgTypeVector args(funcType);
|
ArgTypeVector args(funcType);
|
||||||
for (ABIArgIter i(args); !i.done(); i++) {
|
for (ABIArgIter i(args); !i.done(); i++) {
|
||||||
if (args.isSyntheticStackResultPointerArg(i.index())) {
|
|
||||||
MOZ_CRASH("Exit to function returning multiple values unimplemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
Address dst(masm.getStackPointer(), argOffset + i.index() * sizeof(Value));
|
Address dst(masm.getStackPointer(), argOffset + i.index() * sizeof(Value));
|
||||||
|
|
||||||
MIRType type = i.mirType();
|
MIRType type = i.mirType();
|
||||||
|
MOZ_ASSERT(args.isSyntheticStackResultPointerArg(i.index()) ==
|
||||||
|
(type == MIRType::StackResults));
|
||||||
switch (i->kind()) {
|
switch (i->kind()) {
|
||||||
case ABIArg::GPR:
|
case ABIArg::GPR:
|
||||||
if (type == MIRType::Int32) {
|
if (type == MIRType::Int32) {
|
||||||
|
@ -1648,6 +1653,10 @@ static void FillArgumentArrayForExit(
|
||||||
GenPrintPtr(DebugChannel::Import, masm, i->gpr());
|
GenPrintPtr(DebugChannel::Import, masm, i->gpr());
|
||||||
masm.storePtr(i->gpr(), dst);
|
masm.storePtr(i->gpr(), dst);
|
||||||
}
|
}
|
||||||
|
} else if (type == MIRType::StackResults) {
|
||||||
|
MOZ_ASSERT(!toValue, "Multi-result exit to JIT unimplemented");
|
||||||
|
GenPrintPtr(DebugChannel::Import, masm, i->gpr());
|
||||||
|
masm.storePtr(i->gpr(), dst);
|
||||||
} else {
|
} else {
|
||||||
MOZ_CRASH("FillArgumentArrayForExit, ABIArg::GPR: unexpected type");
|
MOZ_CRASH("FillArgumentArrayForExit, ABIArg::GPR: unexpected type");
|
||||||
}
|
}
|
||||||
|
@ -1878,8 +1887,9 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
|
||||||
// padding between argv and retaddr ensures that sp is aligned.
|
// padding between argv and retaddr ensures that sp is aligned.
|
||||||
unsigned argOffset =
|
unsigned argOffset =
|
||||||
AlignBytes(StackArgBytes(invokeArgTypes), sizeof(double));
|
AlignBytes(StackArgBytes(invokeArgTypes), sizeof(double));
|
||||||
unsigned argBytes =
|
// The abiArgCount includes a stack result pointer argument if needed.
|
||||||
std::max<size_t>(1, fi.funcType().args().length()) * sizeof(Value);
|
unsigned abiArgCount = ArgTypeVector(fi.funcType()).length();
|
||||||
|
unsigned argBytes = std::max<size_t>(1, abiArgCount * sizeof(Value));
|
||||||
unsigned framePushed =
|
unsigned framePushed =
|
||||||
StackDecrementForCall(ABIStackAlignment,
|
StackDecrementForCall(ABIStackAlignment,
|
||||||
sizeof(Frame), // pushed by prologue
|
sizeof(Frame), // pushed by prologue
|
||||||
|
@ -1946,16 +1956,22 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
|
||||||
|
|
||||||
// Make the call, test whether it succeeded, and extract the return value.
|
// Make the call, test whether it succeeded, and extract the return value.
|
||||||
AssertStackAlignment(masm, ABIStackAlignment);
|
AssertStackAlignment(masm, ABIStackAlignment);
|
||||||
const ValTypeVector& results = fi.funcType().results();
|
ResultType resultType = ResultType::Vector(fi.funcType().results());
|
||||||
if (results.length() == 0) {
|
ValType registerResultType;
|
||||||
|
for (ABIResultIter iter(resultType); !iter.done(); iter.next()) {
|
||||||
|
if (iter.cur().inRegister()) {
|
||||||
|
MOZ_ASSERT(!registerResultType.isValid());
|
||||||
|
registerResultType = iter.cur().type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!registerResultType.isValid()) {
|
||||||
masm.call(SymbolicAddress::CallImport_Void);
|
masm.call(SymbolicAddress::CallImport_Void);
|
||||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||||
GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ",
|
GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ",
|
||||||
funcImportIndex);
|
funcImportIndex);
|
||||||
GenPrintf(DebugChannel::Import, masm, "void");
|
GenPrintf(DebugChannel::Import, masm, "void");
|
||||||
} else {
|
} else {
|
||||||
MOZ_ASSERT(results.length() == 1, "multi-value return unimplemented");
|
switch (registerResultType.kind()) {
|
||||||
switch (results[0].kind()) {
|
|
||||||
case ValType::I32:
|
case ValType::I32:
|
||||||
masm.call(SymbolicAddress::CallImport_I32);
|
masm.call(SymbolicAddress::CallImport_I32);
|
||||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||||
|
@ -1990,7 +2006,7 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
|
||||||
GenPrintF64(DebugChannel::Import, masm, ReturnDoubleReg);
|
GenPrintF64(DebugChannel::Import, masm, ReturnDoubleReg);
|
||||||
break;
|
break;
|
||||||
case ValType::Ref:
|
case ValType::Ref:
|
||||||
switch (results[0].refTypeKind()) {
|
switch (registerResultType.refTypeKind()) {
|
||||||
case RefType::Func:
|
case RefType::Func:
|
||||||
masm.call(SymbolicAddress::CallImport_FuncRef);
|
masm.call(SymbolicAddress::CallImport_FuncRef);
|
||||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg,
|
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg,
|
||||||
|
@ -2693,6 +2709,11 @@ bool wasm::GenerateEntryStubs(MacroAssembler& masm, size_t funcExportIndex,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returning multiple values to JS not yet implemented (see bug 1595031).
|
||||||
|
if (fe.funcType().temporarilyUnsupportedResultCountForEntry()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!GenerateJitEntry(masm, funcExportIndex, fe, callee, bigIntEnabled,
|
if (!GenerateJitEntry(masm, funcExportIndex, fe, callee, bigIntEnabled,
|
||||||
&offsets)) {
|
&offsets)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -2737,6 +2758,12 @@ bool wasm::GenerateStubs(const ModuleEnvironment& env,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exit to JS returning multiple values not yet implemented (see bug
|
||||||
|
// 1595031).
|
||||||
|
if (fi.funcType().temporarilyUnsupportedResultCountForExit()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
JitExitOffsets jitOffsets;
|
JitExitOffsets jitOffsets;
|
||||||
if (!GenerateImportJitExit(masm, fi, funcIndex, &throwLabel, &jitOffsets)) {
|
if (!GenerateImportJitExit(masm, fi, funcIndex, &throwLabel, &jitOffsets)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -2464,15 +2464,11 @@ static bool ParseFuncSig(WasmParseContext& c, AstFuncType* funcType,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For ParseAnonFuncType: allow more than one results.
|
|
||||||
enum class MultiResult { False, True };
|
|
||||||
|
|
||||||
// This guarantees that the ref has an index when we return and won't need to be
|
// This guarantees that the ref has an index when we return and won't need to be
|
||||||
// resolved later. If `withLookahead` then whatever comes after the type does
|
// resolved later. If `withLookahead` then whatever comes after the type does
|
||||||
// not cause an error and is not consumed, and any number of results are
|
// not cause an error and is not consumed, and any number of results are
|
||||||
// allowed.
|
// allowed.
|
||||||
static bool ParseAnonFuncType(WasmParseContext& c, AstRef* ref,
|
static bool ParseAnonFuncType(WasmParseContext& c, AstRef* ref,
|
||||||
MultiResult multiResult,
|
|
||||||
WithLookahead withLookahead) {
|
WithLookahead withLookahead) {
|
||||||
MOZ_ASSERT(ref->isInvalid());
|
MOZ_ASSERT(ref->isInvalid());
|
||||||
|
|
||||||
|
@ -2480,10 +2476,6 @@ static bool ParseAnonFuncType(WasmParseContext& c, AstRef* ref,
|
||||||
if (!ParseFuncSig(c, &funcType, withLookahead)) {
|
if (!ParseFuncSig(c, &funcType, withLookahead)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (multiResult == MultiResult::False && funcType.results().length() > 1) {
|
|
||||||
c.ts.generateError(c.ts.peek(), "too many results", c.error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint32_t funcTypeIndex;
|
uint32_t funcTypeIndex;
|
||||||
if (!c.module->declare(std::move(funcType), &funcTypeIndex)) {
|
if (!c.module->declare(std::move(funcType), &funcTypeIndex)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -2525,7 +2517,7 @@ static bool ParseBlockType(WasmParseContext& c, AstBlockType* type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
AstRef t;
|
AstRef t;
|
||||||
if (!ParseAnonFuncType(c, &t, MultiResult::True, WithLookahead::True)) {
|
if (!ParseAnonFuncType(c, &t, WithLookahead::True)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const AstFuncType& ft = c.module->types()[t.index()]->asFuncType();
|
const AstFuncType& ft = c.module->types()[t.index()]->asFuncType();
|
||||||
|
@ -4202,7 +4194,7 @@ static bool ParseFuncType(WasmParseContext& c, AstRef* ref) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ref->isInvalid()) {
|
if (ref->isInvalid()) {
|
||||||
if (!ParseAnonFuncType(c, ref, MultiResult::False, WithLookahead::False)) {
|
if (!ParseAnonFuncType(c, ref, WithLookahead::False)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4289,10 +4281,6 @@ static bool ParseFunc(WasmParseContext& c) {
|
||||||
if (!ParseValueTypeList(c, &results)) {
|
if (!ParseValueTypeList(c, &results)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (results.length() > 1) {
|
|
||||||
c.ts.generateError(token, "too many results", c.error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
c.ts.unget(token);
|
c.ts.unget(token);
|
||||||
|
@ -4382,10 +4370,6 @@ static AstTypeDef* ParseTypeDef(WasmParseContext& c) {
|
||||||
if (!ParseFuncSig(c, &funcType, WithLookahead::False)) {
|
if (!ParseFuncSig(c, &funcType, WithLookahead::False)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (funcType.results().length() > 1) {
|
|
||||||
c.ts.generateError(c.ts.peek(), "too many results", c.error);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
type = new (c.lifo) AstFuncType(name, std::move(funcType));
|
type = new (c.lifo) AstFuncType(name, std::move(funcType));
|
||||||
} else if (c.ts.getIf(WasmToken::Struct)) {
|
} else if (c.ts.getIf(WasmToken::Struct)) {
|
||||||
AstStructType st(c.lifo);
|
AstStructType st(c.lifo);
|
||||||
|
@ -4801,8 +4785,7 @@ static AstImport* ParseImport(WasmParseContext& c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (funcTypeRef.isInvalid()) {
|
if (funcTypeRef.isInvalid()) {
|
||||||
if (!ParseAnonFuncType(c, &funcTypeRef, MultiResult::False,
|
if (!ParseAnonFuncType(c, &funcTypeRef, WithLookahead::False)) {
|
||||||
WithLookahead::False)) {
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1086,6 +1086,16 @@ class FuncType {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Entry from JS to wasm is currently unimplemented for functions that return
|
||||||
|
// multiple values.
|
||||||
|
bool temporarilyUnsupportedResultCountForEntry() const {
|
||||||
|
return results().length() > 1;
|
||||||
|
}
|
||||||
|
// Calls out from wasm to JS that return multiple values is currently
|
||||||
|
// unsupported.
|
||||||
|
bool temporarilyUnsupportedResultCountForExit() const {
|
||||||
|
return results().length() > 1;
|
||||||
|
}
|
||||||
// For JS->wasm jit entries, AnyRef parameters and returns are allowed,
|
// For JS->wasm jit entries, AnyRef parameters and returns are allowed,
|
||||||
// as are all reference types apart from TypeIndex.
|
// as are all reference types apart from TypeIndex.
|
||||||
bool temporarilyUnsupportedReftypeForEntry() const {
|
bool temporarilyUnsupportedReftypeForEntry() const {
|
||||||
|
|
|
@ -1214,8 +1214,6 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env,
|
||||||
bool wasm::ValidateFunctionBody(const ModuleEnvironment& env,
|
bool wasm::ValidateFunctionBody(const ModuleEnvironment& env,
|
||||||
uint32_t funcIndex, uint32_t bodySize,
|
uint32_t funcIndex, uint32_t bodySize,
|
||||||
Decoder& d) {
|
Decoder& d) {
|
||||||
MOZ_ASSERT(env.funcTypes[funcIndex]->results().length() <= MaxFuncResults);
|
|
||||||
|
|
||||||
ValTypeVector locals;
|
ValTypeVector locals;
|
||||||
if (!locals.appendAll(env.funcTypes[funcIndex]->args())) {
|
if (!locals.appendAll(env.funcTypes[funcIndex]->args())) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1590,12 +1588,6 @@ static bool DecodeSignatureIndex(Decoder& d, const TypeDefVector& types,
|
||||||
return d.fail("signature index references non-signature");
|
return d.fail("signature index references non-signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Remove this check when full multi-value function returns land.
|
|
||||||
// Bug 1585909.
|
|
||||||
if (def.funcType().results().length() > MaxFuncResults) {
|
|
||||||
return d.fail("too many returns in signature");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[multiple return values inside wasm]
|
[multiple return values inside wasm]
|
||||||
expected: FAIL
|
expected:
|
||||||
|
if not nightly_build: FAIL
|
||||||
|
|
||||||
[multiple return values from js to wasm]
|
[multiple return values from js to wasm]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
Загрузка…
Ссылка в новой задаче