Commit 921135c7 authored by Thibaud Michaud's avatar Thibaud Michaud Committed by V8 LUCI CQ

[wasm] Handle arguments in stack-switching export wrapper

Use the existing generic js-to-wasm wrapper to handle arguments in
the stack-switching export wrapper, by combining them into a single
helper function parameterized by a boolean.

If the stack_switch parameter is false, the generated js-to-wasm wrapper
is the same as before.

If the stack_switch parameter is true, we allocate and switch to the new
stack before starting to process the parameters. To load the parameters,
we also keep a pointer to the old stack.
After the call, we convert the return value according to the return type
as usual, and then switch back to the parent stack (which may be
different than the original stack, but has a compatible stack frame
layout).
If the stack suspends during the call, control-flow jumps right before
we deconstruct and leave the frame, and returns the Promise as an
externref in the return register.

R=ahaas@chromium.org,jkummerow@chromium.org
CC=fgm@chromium.org

Bug: v8:12191
Change-Id: If3f8eaba8edebe6e98d4738f79f895fdb5322adc
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3460410Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79148}
parent 46c7768b
This diff is collapsed.
......@@ -204,20 +204,18 @@ class BuiltinFrameConstants : public TypedFrameConstants {
DEFINE_TYPED_FRAME_SIZES(2);
};
// Fixed frame slots shared by the js-to-wasm wrapper, the
// ReturnPromiseOnSuspend wrapper and the WasmResume wrapper.
class BuiltinWasmWrapperConstants : public TypedFrameConstants {
public:
// This slot contains the number of slots at the top of the frame that need to
// be scanned by the GC.
static constexpr int kGCScanSlotCountOffset =
TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
};
class ReturnPromiseOnSuspendFrameConstants
: public BuiltinWasmWrapperConstants {
public:
static constexpr int kParamCountOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
static constexpr int kSpillAreaSize =
-(kParamCountOffset - TypedFrameConstants::kFixedFrameSizeFromFp);
// The number of parameters passed to this function.
static constexpr int kInParamCountOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
// The number of parameters according to the signature.
static constexpr int kParamCountOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(2);
};
class ConstructFrameConstants : public TypedFrameConstants {
......
......@@ -87,13 +87,10 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
(function TestStackSwitchSuspend() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addGlobal(kWasmI32, true).exportAs('g');
import_index = builder.addImport('m', 'import', kSig_i_v);
builder.addFunction("test", kSig_i_v)
.addBody([
kExprCallFunction, import_index, // suspend
kExprGlobalSet, 0, // resume
kExprI32Const, 0,
]).exportFunc();
let suspender = new WebAssembly.Suspender();
function js_import() {
......@@ -107,8 +104,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
let instance = builder.instantiate({m: {import: suspending_wasm_js_import}});
let wrapped_export = suspender.returnPromiseOnSuspend(instance.exports.test);
let combined_promise = wrapped_export();
assertEquals(0, instance.exports.g.value);
combined_promise.then(_ => assertEquals(42, instance.exports.g.value));
combined_promise.then(v => assertEquals(42, v));
})();
// Check that we can suspend back out of a resumed computation.
......@@ -193,3 +189,40 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
// TODO(thibaudm): Check the result's value once this is supported.
assertEquals(42, instance.exports.g.value);
})();
(function TestStackSwitchSuspendArgs() {
print(arguments.callee.name);
function reduce(array) {
// a[0] + a[1] * 2 + a[2] * 3 + ...
return array.reduce((prev, cur, i) => prev + cur * (i + 1));
}
let builder = new WasmModuleBuilder();
// Number of param registers + 1 for both types.
let sig = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32, kWasmI32, kWasmI32,
kWasmF32, kWasmF32, kWasmF32, kWasmF32, kWasmF32, kWasmF32, kWasmF32], [kWasmI32]);
import_index = builder.addImport('m', 'import', sig);
builder.addFunction("test", sig)
.addBody([
kExprLocalGet, 0, kExprLocalGet, 1, kExprLocalGet, 2, kExprLocalGet, 3,
kExprLocalGet, 4, kExprLocalGet, 5, kExprLocalGet, 6, kExprLocalGet, 7,
kExprLocalGet, 8, kExprLocalGet, 9, kExprLocalGet, 10, kExprLocalGet, 11,
kExprLocalGet, 12,
kExprCallFunction, import_index, // suspend
]).exportFunc();
let suspender = new WebAssembly.Suspender();
function js_import(i1, i2, i3, i4, i5, i6, f1, f2, f3, f4, f5, f6, f7) {
return Promise.resolve(reduce(Array.from(arguments)));
};
let wasm_js_import = new WebAssembly.Function(
{parameters: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'f32', 'f32',
'f32', 'f32', 'f32', 'f32', 'f32'], results: ['externref']}, js_import);
let suspending_wasm_js_import =
suspender.suspendOnReturnedPromise(wasm_js_import);
let instance = builder.instantiate({m: {import: suspending_wasm_js_import}});
let wrapped_export = suspender.returnPromiseOnSuspend(instance.exports.test);
let args = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
let combined_promise =
wrapped_export.apply(null, args);
combined_promise.then(v => assertEquals(reduce(args), v));
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment