[wasm][stack-switching] Fix nested suspenders
Fix some issues with nested suspenders: - Fix scratch register conflict when returning from an inner suspender - The outer suspender should stay in 'Active' state - Suspenders should become 'Inactive' when they return CC=ahaas@chromium.org Bug: v8:12191 Change-Id: Ic6c6108c4f8df3d32417d7813eb04e0e2a46d27a Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3743386 Reviewed-by: Andreas Haas <ahaas@chromium.org> Commit-Queue: Thibaud Michaud <thibaudm@chromium.org> Cr-Commit-Position: refs/heads/main@{#81555}
This commit is contained in:
parent
e4f07c0997
commit
d0b75e25da
@ -523,6 +523,7 @@ bool Builtins::CodeObjectIsExecutable(Builtin builtin) {
|
|||||||
case Builtin::kInstantiateAsmJs:
|
case Builtin::kInstantiateAsmJs:
|
||||||
#if V8_ENABLE_WEBASSEMBLY
|
#if V8_ENABLE_WEBASSEMBLY
|
||||||
case Builtin::kGenericJSToWasmWrapper:
|
case Builtin::kGenericJSToWasmWrapper:
|
||||||
|
case Builtin::kWasmReturnPromiseOnSuspend:
|
||||||
#endif // V8_ENABLE_WEBASSEMBLY
|
#endif // V8_ENABLE_WEBASSEMBLY
|
||||||
|
|
||||||
// TODO(delphick): Remove this when calls to it have the trampoline inlined
|
// TODO(delphick): Remove this when calls to it have the trampoline inlined
|
||||||
|
@ -3092,21 +3092,25 @@ void ReloadParentContinuation(MacroAssembler* masm, Register wasm_instance,
|
|||||||
__ Pop(return_reg);
|
__ Pop(return_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestoreParentSuspender(MacroAssembler* masm) {
|
void RestoreParentSuspender(MacroAssembler* masm, Register tmp1,
|
||||||
Register suspender = kScratchRegister;
|
Register tmp2) {
|
||||||
|
Register suspender = tmp1;
|
||||||
__ LoadRoot(suspender, RootIndex::kActiveSuspender);
|
__ LoadRoot(suspender, RootIndex::kActiveSuspender);
|
||||||
|
__ StoreTaggedSignedField(
|
||||||
|
FieldOperand(suspender, WasmSuspenderObject::kStateOffset),
|
||||||
|
Smi::FromInt(WasmSuspenderObject::kInactive));
|
||||||
__ LoadAnyTaggedField(
|
__ LoadAnyTaggedField(
|
||||||
suspender, FieldOperand(suspender, WasmSuspenderObject::kParentOffset));
|
suspender, FieldOperand(suspender, WasmSuspenderObject::kParentOffset));
|
||||||
__ CompareRoot(suspender, RootIndex::kUndefinedValue);
|
__ CompareRoot(suspender, RootIndex::kUndefinedValue);
|
||||||
Label undefined;
|
Label undefined;
|
||||||
__ j(equal, &undefined, Label::kNear);
|
__ j(equal, &undefined, Label::kNear);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// Check that the parent suspender is inactive.
|
// Check that the parent suspender is active.
|
||||||
Label parent_inactive;
|
Label parent_inactive;
|
||||||
Register state = rbx;
|
Register state = tmp2;
|
||||||
__ LoadTaggedSignedField(
|
__ LoadTaggedSignedField(
|
||||||
state, FieldOperand(suspender, WasmSuspenderObject::kStateOffset));
|
state, FieldOperand(suspender, WasmSuspenderObject::kStateOffset));
|
||||||
__ SmiCompare(state, Smi::FromInt(WasmSuspenderObject::kInactive));
|
__ SmiCompare(state, Smi::FromInt(WasmSuspenderObject::kActive));
|
||||||
__ j(equal, &parent_inactive, Label::kNear);
|
__ j(equal, &parent_inactive, Label::kNear);
|
||||||
__ Trap();
|
__ Trap();
|
||||||
__ bind(&parent_inactive);
|
__ bind(&parent_inactive);
|
||||||
@ -3754,7 +3758,7 @@ void GenericJSToWasmWrapperHelper(MacroAssembler* masm, bool stack_switch) {
|
|||||||
__ bind(&return_done);
|
__ bind(&return_done);
|
||||||
if (stack_switch) {
|
if (stack_switch) {
|
||||||
ReloadParentContinuation(masm, wasm_instance, return_reg, rbx, rcx);
|
ReloadParentContinuation(masm, wasm_instance, return_reg, rbx, rcx);
|
||||||
RestoreParentSuspender(masm);
|
RestoreParentSuspender(masm, rbx, rcx);
|
||||||
}
|
}
|
||||||
__ bind(&suspend);
|
__ bind(&suspend);
|
||||||
// No need to process the return value if the stack is suspended, there is a
|
// No need to process the return value if the stack is suspended, there is a
|
||||||
|
@ -818,10 +818,6 @@ RUNTIME_FUNCTION(Runtime_WasmAllocateContinuation) {
|
|||||||
FullObjectSlot active_suspender_slot =
|
FullObjectSlot active_suspender_slot =
|
||||||
isolate->roots_table().slot(RootIndex::kActiveSuspender);
|
isolate->roots_table().slot(RootIndex::kActiveSuspender);
|
||||||
suspender->set_parent(HeapObject::cast(*active_suspender_slot));
|
suspender->set_parent(HeapObject::cast(*active_suspender_slot));
|
||||||
if (!(*active_suspender_slot).IsUndefined()) {
|
|
||||||
WasmSuspenderObject::cast(*active_suspender_slot)
|
|
||||||
.set_state(WasmSuspenderObject::kInactive);
|
|
||||||
}
|
|
||||||
suspender->set_state(WasmSuspenderObject::kActive);
|
suspender->set_state(WasmSuspenderObject::kActive);
|
||||||
suspender->set_continuation(*target);
|
suspender->set_continuation(*target);
|
||||||
active_suspender_slot.store(*suspender);
|
active_suspender_slot.store(*suspender);
|
||||||
|
@ -353,11 +353,12 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
|||||||
kExprCallFunction, import_index,
|
kExprCallFunction, import_index,
|
||||||
kExprI32Const, 0
|
kExprI32Const, 0
|
||||||
]).exportFunc();
|
]).exportFunc();
|
||||||
function js_import() {
|
|
||||||
return Promise.resolve(0);
|
|
||||||
}
|
|
||||||
let instance = builder.instantiate({m: {i: js_import}});
|
|
||||||
let suspender = new WebAssembly.Suspender();
|
let suspender = new WebAssembly.Suspender();
|
||||||
|
let i = suspender.suspendOnReturnedPromise(
|
||||||
|
new WebAssembly.Function(
|
||||||
|
{parameters: [], results: ['externref']},
|
||||||
|
() => Promise.resolve(0)));
|
||||||
|
let instance = builder.instantiate({m: {i}});
|
||||||
let wrapped_export = suspender.returnPromiseOnSuspend(instance.exports.test);
|
let wrapped_export = suspender.returnPromiseOnSuspend(instance.exports.test);
|
||||||
let promise1 = wrapped_export();
|
let promise1 = wrapped_export();
|
||||||
// Re-enter the suspender before resolving the promise.
|
// Re-enter the suspender before resolving the promise.
|
||||||
@ -365,14 +366,16 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
|||||||
/re-entering an active\/suspended suspender/);
|
/re-entering an active\/suspended suspender/);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(function TestNestedSuspenders() {
|
function TestNestedSuspenders(suspend) {
|
||||||
// Nest two suspenders. The call chain looks like:
|
// Nest two suspenders. The call chain looks like:
|
||||||
// outer (wasm) -> outer (js) -> inner (wasm) -> inner (js)
|
// outer (wasm) -> outer (js) -> inner (wasm) -> inner (js)
|
||||||
// The inner JS function returns a Promise, which suspends the inner wasm
|
// If 'suspend' is true, the inner JS function returns a Promise, which
|
||||||
// function, which returns a Promise, which suspends the outer wasm function,
|
// suspends the inner wasm function, which returns a Promise, which suspends
|
||||||
// which returns a Promise. The inner Promise resolves first, which resumes
|
// the outer wasm function, which returns a Promise. The inner Promise
|
||||||
// the inner continuation. Then the outer promise resolves which resumes the
|
// resolves first, which resumes the inner continuation. Then the outer
|
||||||
// outer continuation.
|
// promise resolves which resumes the outer continuation.
|
||||||
|
// If 'suspend' is false, the inner JS function returns a regular value and
|
||||||
|
// no computation is suspended.
|
||||||
print(arguments.callee.name);
|
print(arguments.callee.name);
|
||||||
let builder = new WasmModuleBuilder();
|
let builder = new WasmModuleBuilder();
|
||||||
inner_index = builder.addImport('m', 'inner', kSig_i_v);
|
inner_index = builder.addImport('m', 'inner', kSig_i_v);
|
||||||
@ -391,7 +394,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
|||||||
let inner = inner_suspender.suspendOnReturnedPromise(
|
let inner = inner_suspender.suspendOnReturnedPromise(
|
||||||
new WebAssembly.Function(
|
new WebAssembly.Function(
|
||||||
{parameters: [], results: ['externref']},
|
{parameters: [], results: ['externref']},
|
||||||
() => Promise.resolve(42)));
|
() => suspend ? Promise.resolve(42) : 42));
|
||||||
|
|
||||||
let export_inner;
|
let export_inner;
|
||||||
let outer = outer_suspender.suspendOnReturnedPromise(
|
let outer = outer_suspender.suspendOnReturnedPromise(
|
||||||
@ -402,5 +405,39 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
|||||||
let instance = builder.instantiate({m: {inner, outer}});
|
let instance = builder.instantiate({m: {inner, outer}});
|
||||||
export_inner = inner_suspender.returnPromiseOnSuspend(instance.exports.inner);
|
export_inner = inner_suspender.returnPromiseOnSuspend(instance.exports.inner);
|
||||||
let export_outer = outer_suspender.returnPromiseOnSuspend(instance.exports.outer);
|
let export_outer = outer_suspender.returnPromiseOnSuspend(instance.exports.outer);
|
||||||
|
if (suspend) {
|
||||||
assertPromiseResult(export_outer(), v => assertEquals(42, v));
|
assertPromiseResult(export_outer(), v => assertEquals(42, v));
|
||||||
|
} else {
|
||||||
|
assertEquals(export_outer(), 42);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(function TestNestedSuspendersSuspend() {
|
||||||
|
TestNestedSuspenders(true);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function TestNestedSuspendersNoSuspend() {
|
||||||
|
TestNestedSuspenders(false);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function TestReenterInactiveSuspender() {
|
||||||
|
print(arguments.callee.name);
|
||||||
|
let builder = new WasmModuleBuilder();
|
||||||
|
let import_index = builder.addImport("m", "i", kSig_i_v);
|
||||||
|
builder.addFunction("test", kSig_i_v)
|
||||||
|
.addBody([
|
||||||
|
kExprCallFunction, import_index,
|
||||||
|
]).exportFunc();
|
||||||
|
let suspender = new WebAssembly.Suspender();
|
||||||
|
let i = suspender.suspendOnReturnedPromise(
|
||||||
|
new WebAssembly.Function(
|
||||||
|
{parameters: [], results: ['externref']},
|
||||||
|
() => Promise.resolve(0)));
|
||||||
|
let instance = builder.instantiate({m: {i}});
|
||||||
|
let wrapped_export = suspender.returnPromiseOnSuspend(instance.exports.test);
|
||||||
|
assertPromiseResult(
|
||||||
|
wrapped_export(),
|
||||||
|
() => assertPromiseResult(
|
||||||
|
wrapped_export(),
|
||||||
|
v => assertEquals(v, 0)));
|
||||||
})();
|
})();
|
||||||
|
Loading…
Reference in New Issue
Block a user