[wasm] Introduce stack-switching frame type

And make the GC visit spilled references in the frame.

R=ahaas@chromium.org
CC=​fgm@chromium.org

Bug: v8:12191
Change-Id: Ida430f12a6de7658972e7890542fb02f7f7ddbb1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3226784
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77763}
This commit is contained in:
Thibaud Michaud 2021-11-08 14:49:52 +01:00 committed by V8 LUCI CQ
parent 13bcdc5b38
commit 0443eb2ef0
6 changed files with 81 additions and 21 deletions

View File

@ -2962,15 +2962,9 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
// -------------------------------------------
// Compute offsets and prepare for GC.
// -------------------------------------------
// We will have to save a value indicating the GC the number
// of values on the top of the stack that have to be scanned before calling
// the Wasm function.
constexpr int kFrameMarkerOffset = -kSystemPointerSize;
constexpr int kGCScanSlotCountOffset =
kFrameMarkerOffset - kSystemPointerSize;
// The number of parameters passed to this function.
constexpr int kInParamCountOffset =
kGCScanSlotCountOffset - kSystemPointerSize;
BuiltinWasmWrapperConstants::kGCScanSlotCountOffset - kSystemPointerSize;
// The number of parameters according to the signature.
constexpr int kParamCountOffset = kInParamCountOffset - kSystemPointerSize;
constexpr int kReturnCountOffset = kParamCountOffset - kSystemPointerSize;
@ -3387,7 +3381,8 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
// We set the indicating value for the GC to the proper one for Wasm call.
constexpr int kWasmCallGCScanSlotCount = 0;
__ Move(MemOperand(rbp, kGCScanSlotCountOffset), kWasmCallGCScanSlotCount);
__ Move(MemOperand(rbp, BuiltinWasmWrapperConstants::kGCScanSlotCountOffset),
kWasmCallGCScanSlotCount);
// -------------------------------------------
// Call the Wasm function.
@ -3470,10 +3465,12 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
// The builtin expects the parameter to be in register param = rax.
constexpr int kBuiltinCallGCScanSlotCount = 2;
PrepareForBuiltinCall(masm, MemOperand(rbp, kGCScanSlotCountOffset),
kBuiltinCallGCScanSlotCount, current_param, param_limit,
current_int_param_slot, current_float_param_slot,
valuetypes_array_ptr, wasm_instance, function_data);
PrepareForBuiltinCall(
masm,
MemOperand(rbp, BuiltinWasmWrapperConstants::kGCScanSlotCountOffset),
kBuiltinCallGCScanSlotCount, current_param, param_limit,
current_int_param_slot, current_float_param_slot, valuetypes_array_ptr,
wasm_instance, function_data);
Label param_kWasmI32_not_smi;
Label param_kWasmI64;
@ -3620,7 +3617,8 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
// -------------------------------------------
__ bind(&compile_wrapper);
// Enable GC.
MemOperand GCScanSlotPlace = MemOperand(rbp, kGCScanSlotCountOffset);
MemOperand GCScanSlotPlace =
MemOperand(rbp, BuiltinWasmWrapperConstants::kGCScanSlotCountOffset);
__ Move(GCScanSlotPlace, 4);
// Save registers to the stack.
__ pushq(wasm_instance);
@ -3651,7 +3649,7 @@ void LoadJumpBuffer(MacroAssembler* masm, Register jmpbuf) {
void Builtins::Generate_WasmReturnPromiseOnSuspend(MacroAssembler* masm) {
// Set up the stackframe.
__ EnterFrame(StackFrame::JS_TO_WASM);
__ EnterFrame(StackFrame::RETURN_PROMISE_ON_SUSPEND);
// Parameters.
Register closure = kJSFunctionRegister; // rdi
@ -3661,7 +3659,10 @@ void Builtins::Generate_WasmReturnPromiseOnSuspend(MacroAssembler* masm) {
}
constexpr int kFrameMarkerOffset = -kSystemPointerSize;
constexpr int kParamCountOffset = kFrameMarkerOffset - kSystemPointerSize;
// This slot contains the number of slots at the top of the frame that need to
// be scanned by the GC.
constexpr int kParamCountOffset =
BuiltinWasmWrapperConstants::kGCScanSlotCountOffset - kSystemPointerSize;
// The frame marker is not included in the slot count.
constexpr int kNumSpillSlots =
-(kParamCountOffset - kFrameMarkerOffset) / kSystemPointerSize;
@ -3723,11 +3724,13 @@ void Builtins::Generate_WasmReturnPromiseOnSuspend(MacroAssembler* masm) {
// -------------------------------------------
// Allocate a new continuation.
// -------------------------------------------
MemOperand GCScanSlotPlace =
MemOperand(rbp, BuiltinWasmWrapperConstants::kGCScanSlotCountOffset);
__ Move(GCScanSlotPlace, 3);
__ Push(wasm_instance);
__ Push(function_data);
__ Push(wasm_instance);
__ Move(kContextRegister, Smi::zero());
// TODO(thibaudm): Handle GC.
__ CallRuntime(Runtime::kWasmAllocateContinuation);
__ Pop(function_data);
__ Pop(wasm_instance);
@ -3750,6 +3753,14 @@ void Builtins::Generate_WasmReturnPromiseOnSuspend(MacroAssembler* masm) {
// Switch stack!
LoadJumpBuffer(masm, target_jmpbuf);
__ movq(rbp, rsp); // New stack, there is no frame yet.
__ Move(GCScanSlotPlace, 3);
__ Push(wasm_instance);
__ Push(function_data);
__ Push(wasm_instance);
__ Move(kContextRegister, Smi::zero());
__ CallRuntime(Runtime::kWasmSyncStackLimit);
__ Pop(function_data);
__ Pop(wasm_instance);
foreign_jmpbuf = no_reg;
target_jmpbuf = no_reg;
// live: [rsi, rdi]
@ -3827,6 +3838,7 @@ void Builtins::Generate_WasmReturnPromiseOnSuspend(MacroAssembler* masm) {
// Switch stack!
LoadJumpBuffer(masm, jmpbuf);
__ leaq(rbp, Operand(rsp, (kNumSpillSlots + 1) * kSystemPointerSize));
__ Move(GCScanSlotPlace, 2);
__ Push(wasm_instance); // Spill.
__ Push(wasm_instance); // First arg.
__ Move(kContextRegister, Smi::zero());
@ -3841,7 +3853,7 @@ void Builtins::Generate_WasmReturnPromiseOnSuspend(MacroAssembler* masm) {
// Epilogue.
// -------------------------------------------
__ movq(param_count, MemOperand(rbp, kParamCountOffset));
__ LeaveFrame(StackFrame::JS_TO_WASM);
__ LeaveFrame(StackFrame::RETURN_PROMISE_ON_SUSPEND);
__ DropArguments(param_count, r8, TurboAssembler::kCountIsInteger,
TurboAssembler::kCountExcludesReceiver);
__ ret(0);

View File

@ -204,6 +204,14 @@ class BuiltinFrameConstants : public TypedFrameConstants {
DEFINE_TYPED_FRAME_SIZES(2);
};
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 =
kFrameTypeOffset - kSystemPointerSize;
};
class ConstructFrameConstants : public TypedFrameConstants {
public:
// FP-relative.

View File

@ -239,6 +239,10 @@ inline WasmToJsFrame::WasmToJsFrame(StackFrameIteratorBase* iterator)
inline JsToWasmFrame::JsToWasmFrame(StackFrameIteratorBase* iterator)
: StubFrame(iterator) {}
inline ReturnPromiseOnSuspendFrame::ReturnPromiseOnSuspendFrame(
StackFrameIteratorBase* iterator)
: StubFrame(iterator) {}
inline CWasmEntryFrame::CWasmEntryFrame(StackFrameIteratorBase* iterator)
: StubFrame(iterator) {}

View File

@ -680,6 +680,7 @@ StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator,
case WASM_EXIT:
case WASM_DEBUG_BREAK:
case JS_TO_WASM:
case RETURN_PROMISE_ON_SUSPEND:
#endif // V8_ENABLE_WEBASSEMBLY
return candidate;
case OPTIMIZED:
@ -1036,6 +1037,7 @@ void CommonFrame::IterateCompiledFrame(RootVisitor* v) const {
case CONSTRUCT:
#if V8_ENABLE_WEBASSEMBLY
case JS_TO_WASM:
case RETURN_PROMISE_ON_SUSPEND:
case C_WASM_ENTRY:
case WASM_DEBUG_BREAK:
#endif // V8_ENABLE_WEBASSEMBLY
@ -2084,10 +2086,29 @@ void JsToWasmFrame::Iterate(RootVisitor* v) const {
IterateCompiledFrame(v);
return;
}
// The [fp - 2*kSystemPointerSize] on the stack is a value indicating how
// many values should be scanned from the top.
intptr_t scan_count =
*reinterpret_cast<intptr_t*>(fp() - 2 * kSystemPointerSize);
// The [fp + BuiltinFrameConstants::kGCScanSlotCount] on the stack is a value
// indicating how many values should be scanned from the top.
intptr_t scan_count = *reinterpret_cast<intptr_t*>(
fp() + BuiltinWasmWrapperConstants::kGCScanSlotCountOffset);
FullObjectSlot spill_slot_base(&Memory<Address>(sp()));
FullObjectSlot spill_slot_limit(
&Memory<Address>(sp() + scan_count * kSystemPointerSize));
v->VisitRootPointers(Root::kStackRoots, nullptr, spill_slot_base,
spill_slot_limit);
}
void ReturnPromiseOnSuspendFrame::Iterate(RootVisitor* v) const {
// See JsToWasmFrame layout.
#ifdef DEBUG
Code code = GetContainingCode(isolate(), pc());
DCHECK(code.is_builtin() &&
code.builtin_id() == Builtin::kWasmReturnPromiseOnSuspend);
#endif
// The [fp + BuiltinFrameConstants::kGCScanSlotCountOffset] on the stack is a
// value indicating how many values should be scanned from the top.
intptr_t scan_count = *reinterpret_cast<intptr_t*>(
fp() + BuiltinWasmWrapperConstants::kGCScanSlotCountOffset);
FullObjectSlot spill_slot_base(&Memory<Address>(sp()));
FullObjectSlot spill_slot_limit(

View File

@ -100,6 +100,7 @@ class StackHandler {
IF_WASM(V, WASM, WasmFrame) \
IF_WASM(V, WASM_TO_JS, WasmToJsFrame) \
IF_WASM(V, JS_TO_WASM, JsToWasmFrame) \
IF_WASM(V, RETURN_PROMISE_ON_SUSPEND, ReturnPromiseOnSuspendFrame) \
IF_WASM(V, WASM_DEBUG_BREAK, WasmDebugBreakFrame) \
IF_WASM(V, C_WASM_ENTRY, CWasmEntryFrame) \
IF_WASM(V, WASM_EXIT, WasmExitFrame) \
@ -1045,6 +1046,19 @@ class JsToWasmFrame : public StubFrame {
friend class StackFrameIteratorBase;
};
class ReturnPromiseOnSuspendFrame : public StubFrame {
public:
Type type() const override { return RETURN_PROMISE_ON_SUSPEND; }
void Iterate(RootVisitor* v) const override;
protected:
inline explicit ReturnPromiseOnSuspendFrame(StackFrameIteratorBase* iterator);
private:
friend class StackFrameIteratorBase;
};
class CWasmEntryFrame : public StubFrame {
public:
Type type() const override { return C_WASM_ENTRY; }

View File

@ -542,6 +542,7 @@ FRAME_MARKERS = (
"WASM",
"WASM_TO_JS",
"JS_TO_WASM",
"RETURN_PROMISE_ON_SUSPEND",
"WASM_DEBUG_BREAK",
"C_WASM_ENTRY",
"WASM_EXIT",