diff --git a/src/builtins/x64/builtins-x64.cc b/src/builtins/x64/builtins-x64.cc index 442fd33cc4..d4546b5a56 100644 --- a/src/builtins/x64/builtins-x64.cc +++ b/src/builtins/x64/builtins-x64.cc @@ -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)); diff --git a/test/mjsunit/wasm/stack-switching.js b/test/mjsunit/wasm/stack-switching.js index bb69786389..e40c7b1951 100644 --- a/test/mjsunit/wasm/stack-switching.js +++ b/test/mjsunit/wasm/stack-switching.js @@ -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)); +})();