[wasm] Support multi-return WASM function calls from JS

Calling a multi-return WASM function from JS creates an array filled
with the returned values.

See: https://github.com/WebAssembly/multi-value

R=ahaas@chromium.org

Bug: v8:9492
Change-Id: I3151212b6784782c8f89908befab9d26b32e5a8b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1739372
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63110}
This commit is contained in:
Thibaud Michaud 2019-08-07 11:10:32 +02:00 committed by Commit Bot
parent 42fd0bfd39
commit 179ed98857
5 changed files with 94 additions and 26 deletions

View File

@ -5606,9 +5606,25 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
// Clear the ThreadInWasm flag.
BuildModifyThreadInWasmFlag(false);
Node* jsval = sig_->return_count() == 0
? BuildLoadUndefinedValueFromInstance()
: ToJS(rets[0], sig_->GetReturn());
Node* jsval;
if (sig_->return_count() == 0) {
jsval = BuildLoadUndefinedValueFromInstance();
} else if (sig_->return_count() == 1) {
jsval = ToJS(rets[0], sig_->GetReturn());
} else {
int32_t return_count = static_cast<int32_t>(sig_->return_count());
Node* size = jsgraph()->SmiConstant(return_count);
// TODO(thibaudm): Replace runtime calls with TurboFan code.
Node* fixed_array =
BuildCallToRuntime(Runtime::kWasmNewMultiReturnFixedArray, &size, 1);
for (int i = 0; i < return_count; ++i) {
Node* value = ToJS(rets[i], sig_->GetReturn(i));
STORE_FIXED_ARRAY_SLOT_ANY(fixed_array, i, value);
}
jsval = BuildCallToRuntimeWithContext(Runtime::kWasmNewMultiReturnJSArray,
js_context, &fixed_array, 1,
effect_, Control());
}
Return(jsval);
}

View File

@ -568,5 +568,24 @@ RUNTIME_FUNCTION(Runtime_WasmTableFill) {
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_WasmNewMultiReturnFixedArray) {
DCHECK_EQ(1, args.length());
HandleScope scope(isolate);
CONVERT_INT32_ARG_CHECKED(size, 0);
Handle<FixedArray> fixed_array = isolate->factory()->NewFixedArray(size);
return *fixed_array;
}
RUNTIME_FUNCTION(Runtime_WasmNewMultiReturnJSArray) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
DCHECK(!isolate->context().is_null());
CONVERT_ARG_CHECKED(FixedArray, fixed_array, 0);
Handle<FixedArray> fixed_array_handle(fixed_array, isolate);
Handle<JSArray> array = isolate->factory()->NewJSArrayWithElements(
fixed_array_handle, PACKED_ELEMENTS);
return *array;
}
} // namespace internal
} // namespace v8

View File

@ -529,28 +529,30 @@ namespace internal {
F(TypedArraySet, 2, 1) \
F(TypedArraySortFast, 1, 1)
#define FOR_EACH_INTRINSIC_WASM(F, I) \
F(ThrowWasmError, 1, 1) \
F(ThrowWasmStackOverflow, 0, 1) \
F(WasmI32AtomicWait, 4, 1) \
F(WasmI64AtomicWait, 5, 1) \
F(WasmAtomicNotify, 3, 1) \
F(WasmExceptionGetValues, 1, 1) \
F(WasmExceptionGetTag, 1, 1) \
F(WasmMemoryGrow, 2, 1) \
F(WasmRunInterpreter, 2, 1) \
F(WasmStackGuard, 0, 1) \
F(WasmThrowCreate, 2, 1) \
F(WasmThrowTypeError, 0, 1) \
F(WasmRefFunc, 1, 1) \
F(WasmFunctionTableGet, 3, 1) \
F(WasmFunctionTableSet, 4, 1) \
F(WasmTableInit, 5, 1) \
F(WasmTableCopy, 5, 1) \
F(WasmTableGrow, 3, 1) \
F(WasmTableFill, 4, 1) \
F(WasmIsValidFuncRefValue, 1, 1) \
F(WasmCompileLazy, 2, 1)
#define FOR_EACH_INTRINSIC_WASM(F, I) \
F(ThrowWasmError, 1, 1) \
F(ThrowWasmStackOverflow, 0, 1) \
F(WasmI32AtomicWait, 4, 1) \
F(WasmI64AtomicWait, 5, 1) \
F(WasmAtomicNotify, 3, 1) \
F(WasmExceptionGetValues, 1, 1) \
F(WasmExceptionGetTag, 1, 1) \
F(WasmMemoryGrow, 2, 1) \
F(WasmRunInterpreter, 2, 1) \
F(WasmStackGuard, 0, 1) \
F(WasmThrowCreate, 2, 1) \
F(WasmThrowTypeError, 0, 1) \
F(WasmRefFunc, 1, 1) \
F(WasmFunctionTableGet, 3, 1) \
F(WasmFunctionTableSet, 4, 1) \
F(WasmTableInit, 5, 1) \
F(WasmTableCopy, 5, 1) \
F(WasmTableGrow, 3, 1) \
F(WasmTableFill, 4, 1) \
F(WasmIsValidFuncRefValue, 1, 1) \
F(WasmCompileLazy, 2, 1) \
F(WasmNewMultiReturnFixedArray, 1, 1) \
F(WasmNewMultiReturnJSArray, 1, 1)
#define FOR_EACH_INTRINSIC_RETURN_PAIR_IMPL(F, I) \
F(DebugBreakOnBytecode, 1, 2) \

View File

@ -447,7 +447,7 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& sig) {
}
bool IsJSCompatibleSignature(const FunctionSig* sig, bool has_bigint_feature) {
if (sig->return_count() > 1) {
if (!FLAG_experimental_wasm_mv && sig->return_count() > 1) {
return false;
}
for (auto type : sig->all()) {

View File

@ -319,3 +319,34 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
assertEquals(instance.exports.main(2), 8);
assertEquals(instance.exports.main(10), 200);
})();
(function MultiJSReturnTest() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_fi_if = makeSig([kWasmI32, kWasmF32], [kWasmF32, kWasmI32]);
builder.addFunction("swap", sig_fi_if)
.addBody([
kExprGetLocal, 1,
kExprGetLocal, 0])
.exportAs("swap");
builder.addFunction("addsubmul", kSig_iii_i)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 0,
kExprI32Add,
kExprGetLocal, 0,
kExprGetLocal, 0,
kExprI32Sub,
kExprGetLocal, 0,
kExprGetLocal, 0,
kExprI32Mul])
.exportAs("addsubmul");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(instance.exports.swap(0, 1.5), [1.5, 0]);
assertEquals(instance.exports.swap(2, 3.75), [3.75, 2]);
assertEquals(instance.exports.addsubmul(4), [8, 0, 16]);
assertEquals(instance.exports.addsubmul(5), [10, 0, 25]);
})();