Commit 72b0756e authored by Clemens Backes's avatar Clemens Backes Committed by V8 LUCI CQ

[liftoff][ia32] Add explicit stack check for large frames

Add an explicit check for the available stack space before allocating a
large frame. Even though this typically does not cause problems on ia32,
we should do it to be consistent with other platforms and with TurboFan
code.

This follows the same structure as on x64: https://crrev.com/c/3059074

A follow-up CL will add a DCHECK to verify that we never overflow the
stack space by more than 4KB (https://crrev.com/c/3059076).

R=ahaas@chromium.org

Bug: v8:12017
Change-Id: Ifffe56f29feae14545e6f70e30a1c94c5eabad6f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3059075Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75991}
parent bbeccc35
......@@ -11,6 +11,7 @@
#include "src/wasm/baseline/liftoff-assembler.h"
#include "src/wasm/simd-shuffle.h"
#include "src/wasm/value-type.h"
#include "src/wasm/wasm-objects.h"
namespace v8 {
namespace internal {
......@@ -220,44 +221,82 @@ void LiftoffAssembler::PrepareTailCall(int num_callee_stack_params,
void LiftoffAssembler::AlignFrameSize() {}
void LiftoffAssembler::PatchPrepareStackFrame(int offset,
SafepointTableBuilder*) {
void LiftoffAssembler::PatchPrepareStackFrame(
int offset, SafepointTableBuilder* safepoint_table_builder) {
// The frame_size includes the frame marker and the instance slot. Both are
// pushed as part of frame construction, so we don't need to allocate memory
// for them anymore.
int frame_size = GetTotalFrameSize() - 2 * kSystemPointerSize;
DCHECK_EQ(frame_size % kSystemPointerSize, 0);
// We can't run out of space, just pass anything big enough to not cause the
// assembler to try to grow the buffer.
DCHECK_EQ(0, frame_size % kSystemPointerSize);
// We can't run out of space when patching, just pass anything big enough to
// not cause the assembler to try to grow the buffer.
constexpr int kAvailableSpace = 64;
Assembler patching_assembler(
AssemblerOptions{},
ExternalAssemblerBuffer(buffer_start_ + offset, kAvailableSpace));
#if V8_OS_WIN
if (frame_size > kStackPageSize) {
// Generate OOL code (at the end of the function, where the current
// assembler is pointing) to do the explicit stack limit check (see
// https://docs.microsoft.com/en-us/previous-versions/visualstudio/
// visual-studio-6.0/aa227153(v=vs.60)).
// At the function start, emit a jump to that OOL code (from {offset} to
// {pc_offset()}).
int ool_offset = pc_offset() - offset;
patching_assembler.jmp_rel(ool_offset);
DCHECK_GE(liftoff::kSubSpSize, patching_assembler.pc_offset());
patching_assembler.Nop(liftoff::kSubSpSize -
patching_assembler.pc_offset());
// Now generate the OOL code.
AllocateStackSpace(frame_size);
// Jump back to the start of the function (from {pc_offset()} to {offset +
// kSubSpSize}).
int func_start_offset = offset + liftoff::kSubSpSize - pc_offset();
jmp_rel(func_start_offset);
if (V8_LIKELY(frame_size < 4 * KB)) {
// This is the standard case for small frames: just subtract from SP and be
// done with it.
patching_assembler.sub_sp_32(frame_size);
DCHECK_EQ(liftoff::kSubSpSize, patching_assembler.pc_offset());
return;
}
#endif
patching_assembler.sub_sp_32(frame_size);
DCHECK_EQ(liftoff::kSubSpSize, patching_assembler.pc_offset());
// The frame size is bigger than 4KB, so we might overflow the available stack
// space if we first allocate the frame and then do the stack check (we will
// need some remaining stack space for throwing the exception). That's why we
// check the available stack space before we allocate the frame. To do this we
// replace the {__ sub(sp, framesize)} with a jump to OOL code that does this
// "extended stack check".
//
// The OOL code can simply be generated here with the normal assembler,
// because all other code generation, including OOL code, has already finished
// when {PatchPrepareStackFrame} is called. The function prologue then jumps
// to the current {pc_offset()} to execute the OOL code for allocating the
// large frame.
// Emit the unconditional branch in the function prologue (from {offset} to
// {pc_offset()}).
patching_assembler.jmp_rel(pc_offset() - offset);
DCHECK_GE(liftoff::kSubSpSize, patching_assembler.pc_offset());
patching_assembler.Nop(liftoff::kSubSpSize - patching_assembler.pc_offset());
// If the frame is bigger than the stack, we throw the stack overflow
// exception unconditionally. Thereby we can avoid the integer overflow
// check in the condition code.
Label continuation;
if (frame_size < FLAG_stack_size * 1024) {
// We do not have a scratch register, so pick any and push it first.
Register stack_limit = eax;
push(stack_limit);
mov(stack_limit,
FieldOperand(kWasmInstanceRegister,
WasmInstanceObject::kRealStackLimitAddressOffset));
mov(stack_limit, Operand(stack_limit, 0));
add(stack_limit, Immediate(frame_size));
cmp(esp, stack_limit);
pop(stack_limit);
j(above_equal, &continuation, Label::kNear);
}
wasm_call(wasm::WasmCode::kWasmStackOverflow, RelocInfo::WASM_STUB_CALL);
// The call will not return; just define an empty safepoint.
safepoint_table_builder->DefineSafepoint(this);
AssertUnreachable(AbortReason::kUnexpectedReturnFromWasmTrap);
bind(&continuation);
// Now allocate the stack space. Note that this might do more than just
// decrementing the SP; consult {TurboAssembler::AllocateStackSpace}.
AllocateStackSpace(frame_size);
// Jump back to the start of the function, from {pc_offset()} to
// right after the reserved space for the {__ sub(sp, sp, framesize)} (which
// is a branch now).
int func_start_offset = offset + liftoff::kSubSpSize;
jmp_rel(func_start_offset - pc_offset());
}
void LiftoffAssembler::FinishCode() {}
......
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