[wasm] Update suspender's parent on resume

Only the parent of the continuation itself was updated. Also update the
parent of the suspender

R=ahaas@chromium.org

Bug: v8:12191
Change-Id: I06684548abe70f4bbda48c12e9e8adda84e1ec27
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3726293
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81419}
This commit is contained in:
Thibaud Michaud 2022-06-28 18:21:51 +02:00 committed by V8 LUCI CQ
parent 80575e2816
commit 5ef3bb9b58
2 changed files with 60 additions and 12 deletions

View File

@ -4157,7 +4157,9 @@ void Generate_WasmResumeHelper(MacroAssembler* masm, wasm::OnResume on_resume) {
__ LoadAnyTaggedField(
function_data,
FieldOperand(sfi, SharedFunctionInfo::kFunctionDataOffset));
Register suspender = rax;
// The write barrier uses a fixed register for the host object (rdi). The next
// barrier is on the suspender, so load it in rdi directly.
Register suspender = rdi;
__ LoadAnyTaggedField(
suspender, FieldOperand(function_data, WasmResumeData::kSuspenderOffset));
// Check the suspender state.
@ -4178,7 +4180,7 @@ void Generate_WasmResumeHelper(MacroAssembler* masm, wasm::OnResume on_resume) {
Label suspend;
Register active_continuation = r9;
__ LoadRoot(active_continuation, RootIndex::kActiveContinuation);
Register current_jmpbuf = rdi;
Register current_jmpbuf = rax;
__ LoadAnyTaggedField(
current_jmpbuf,
FieldOperand(active_continuation, WasmContinuationObject::kJmpbufOffset));
@ -4190,16 +4192,28 @@ void Generate_WasmResumeHelper(MacroAssembler* masm, wasm::OnResume on_resume) {
current_jmpbuf = no_reg;
// -------------------------------------------
// Set suspender's parent to active continuation.
// Set the suspender and continuation parents and update the roots
// -------------------------------------------
Register active_suspender = rcx;
Register slot_address = WriteBarrierDescriptor::SlotAddressRegister();
// Check that the fixed register isn't one that is already in use.
DCHECK(slot_address == rbx || slot_address == r8);
__ LoadRoot(active_suspender, RootIndex::kActiveSuspender);
__ StoreTaggedField(
FieldOperand(suspender, WasmSuspenderObject::kParentOffset),
active_suspender);
__ RecordWriteField(suspender, WasmSuspenderObject::kParentOffset,
active_suspender, slot_address, SaveFPRegsMode::kIgnore);
__ StoreTaggedSignedField(
FieldOperand(suspender, WasmSuspenderObject::kStateOffset),
Smi::FromInt(WasmSuspenderObject::kActive));
Register target_continuation = rdi;
__ movq(masm->RootAsOperand(RootIndex::kActiveSuspender), suspender);
Register target_continuation = suspender;
__ LoadAnyTaggedField(
target_continuation,
FieldOperand(suspender, WasmSuspenderObject::kContinuationOffset));
Register slot_address = WriteBarrierDescriptor::SlotAddressRegister();
suspender = no_reg;
__ StoreTaggedField(
FieldOperand(target_continuation, WasmContinuationObject::kParentOffset),
active_continuation);
@ -4207,14 +4221,8 @@ void Generate_WasmResumeHelper(MacroAssembler* masm, wasm::OnResume on_resume) {
target_continuation, WasmContinuationObject::kParentOffset,
active_continuation, slot_address, SaveFPRegsMode::kIgnore);
active_continuation = no_reg;
// -------------------------------------------
// Update roots.
// -------------------------------------------
__ movq(masm->RootAsOperand(RootIndex::kActiveContinuation),
target_continuation);
__ movq(masm->RootAsOperand(RootIndex::kActiveSuspender), suspender);
suspender = no_reg;
MemOperand GCScanSlotPlace =
MemOperand(rbp, BuiltinWasmWrapperConstants::kGCScanSlotCountOffset);
@ -4227,7 +4235,7 @@ void Generate_WasmResumeHelper(MacroAssembler* masm, wasm::OnResume on_resume) {
// -------------------------------------------
// Load state from target jmpbuf (longjmp).
// -------------------------------------------
Register target_jmpbuf = target_continuation;
Register target_jmpbuf = rdi;
__ LoadAnyTaggedField(
target_jmpbuf,
FieldOperand(target_continuation, WasmContinuationObject::kJmpbufOffset));

View File

@ -364,3 +364,43 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
assertThrows(wrapped_export, WebAssembly.RuntimeError,
/re-entering an active\/suspended suspender/);
})();
(function TestNestedSuspenders() {
// Nest two suspenders. The call chain looks like:
// outer (wasm) -> outer (js) -> inner (wasm) -> inner (js)
// The inner JS function returns a Promise, which suspends the inner wasm
// function, which returns a Promise, which suspends the outer wasm function,
// which returns a Promise. The inner Promise resolves first, which resumes
// the inner continuation. Then the outer promise resolves which resumes the
// outer continuation.
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
inner_index = builder.addImport('m', 'inner', kSig_i_v);
outer_index = builder.addImport('m', 'outer', kSig_i_v);
builder.addFunction("outer", kSig_i_v)
.addBody([
kExprCallFunction, outer_index
]).exportFunc();
builder.addFunction("inner", kSig_i_v)
.addBody([
kExprCallFunction, inner_index
]).exportFunc();
let outer_suspender = new WebAssembly.Suspender();
let inner_suspender = new WebAssembly.Suspender();
let inner = inner_suspender.suspendOnReturnedPromise(
new WebAssembly.Function(
{parameters: [], results: ['externref']},
() => Promise.resolve(42)));
let export_inner;
let outer = outer_suspender.suspendOnReturnedPromise(
new WebAssembly.Function(
{parameters: [], results: ['externref']},
() => export_inner()));
let instance = builder.instantiate({m: {inner, outer}});
export_inner = inner_suspender.returnPromiseOnSuspend(instance.exports.inner);
let export_outer = outer_suspender.returnPromiseOnSuspend(instance.exports.outer);
assertPromiseResult(export_outer(), v => assertEquals(42, v));
})();