Commit 5a55f36b authored by Liu Yu's avatar Liu Yu Committed by V8 LUCI CQ

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

Port edc349db

Bug: v8:11235

Change-Id: Ie3cfadf97afcea4048c20bc1a5646f4e3c2a82ae
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3058061Reviewed-by: 's avatarZhao Jiazhong <zhaojiazhong-hf@loongson.cn>
Commit-Queue: Zhao Jiazhong <zhaojiazhong-hf@loongson.cn>
Auto-Submit: Liu yu <liuyu@loongson.cn>
Cr-Commit-Position: refs/heads/master@{#75950}
parent e8fd9368
...@@ -4053,6 +4053,16 @@ void TurboAssembler::BranchLong(Label* L, BranchDelaySlot bdslot) { ...@@ -4053,6 +4053,16 @@ void TurboAssembler::BranchLong(Label* L, BranchDelaySlot bdslot) {
} }
} }
void TurboAssembler::BranchLong(int32_t offset, BranchDelaySlot bdslot) {
if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT && (is_int26(offset))) {
BranchShortHelperR6(offset, nullptr);
} else {
// Generate position independent long branch.
BlockTrampolinePoolScope block_trampoline_pool(this);
GenPCRelativeJump(t8, t9, offset, RelocInfo::NONE, bdslot);
}
}
void TurboAssembler::BranchAndLinkLong(Label* L, BranchDelaySlot bdslot) { void TurboAssembler::BranchAndLinkLong(Label* L, BranchDelaySlot bdslot) {
if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT && if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT &&
(!L->is_bound() || is_near_r6(L))) { (!L->is_bound() || is_near_r6(L))) {
......
...@@ -176,6 +176,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { ...@@ -176,6 +176,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
void BranchMSA(Label* target, MSABranchDF df, MSABranchCondition cond, void BranchMSA(Label* target, MSABranchDF df, MSABranchCondition cond,
MSARegister wt, BranchDelaySlot bd = PROTECT); MSARegister wt, BranchDelaySlot bd = PROTECT);
void BranchLong(int32_t offset, BranchDelaySlot bdslot = PROTECT);
void Branch(Label* L, Condition cond, Register rs, RootIndex index, void Branch(Label* L, Condition cond, Register rs, RootIndex index,
BranchDelaySlot bdslot = PROTECT); BranchDelaySlot bdslot = PROTECT);
......
...@@ -4556,6 +4556,25 @@ void TurboAssembler::BranchLong(Label* L, BranchDelaySlot bdslot) { ...@@ -4556,6 +4556,25 @@ void TurboAssembler::BranchLong(Label* L, BranchDelaySlot bdslot) {
} }
} }
void TurboAssembler::BranchLong(int32_t offset, BranchDelaySlot bdslot) {
if (kArchVariant == kMips64r6 && bdslot == PROTECT && (is_int26(offset))) {
BranchShortHelperR6(offset, nullptr);
} else {
BlockTrampolinePoolScope block_trampoline_pool(this);
or_(t8, ra, zero_reg);
nal(); // Read PC into ra register.
lui(t9, (offset & kHiMaskOf32) >> kLuiShift); // Branch delay slot.
ori(t9, t9, (offset & kImm16Mask));
daddu(t9, ra, t9);
if (bdslot == USE_DELAY_SLOT) {
or_(ra, t8, zero_reg);
}
jr(t9);
// Emit a or_ in the branch delay slot if it's protected.
if (bdslot == PROTECT) or_(ra, t8, zero_reg);
}
}
void TurboAssembler::BranchAndLinkLong(Label* L, BranchDelaySlot bdslot) { void TurboAssembler::BranchAndLinkLong(Label* L, BranchDelaySlot bdslot) {
if (kArchVariant == kMips64r6 && bdslot == PROTECT && if (kArchVariant == kMips64r6 && bdslot == PROTECT &&
(!L->is_bound() || is_near_r6(L))) { (!L->is_bound() || is_near_r6(L))) {
......
...@@ -197,6 +197,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { ...@@ -197,6 +197,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
void BranchMSA(Label* target, MSABranchDF df, MSABranchCondition cond, void BranchMSA(Label* target, MSABranchDF df, MSABranchCondition cond,
MSARegister wt, BranchDelaySlot bd = PROTECT); MSARegister wt, BranchDelaySlot bd = PROTECT);
void BranchLong(int32_t offset, BranchDelaySlot bdslot = PROTECT);
void Branch(Label* L, Condition cond, Register rs, RootIndex index, void Branch(Label* L, Condition cond, Register rs, RootIndex index,
BranchDelaySlot bdslot = PROTECT); BranchDelaySlot bdslot = PROTECT);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "src/base/platform/wrappers.h" #include "src/base/platform/wrappers.h"
#include "src/heap/memory-chunk.h" #include "src/heap/memory-chunk.h"
#include "src/wasm/baseline/liftoff-assembler.h" #include "src/wasm/baseline/liftoff-assembler.h"
#include "src/wasm/wasm-objects.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -304,12 +305,15 @@ inline void ChangeEndiannessStore(LiftoffAssembler* assm, LiftoffRegister src, ...@@ -304,12 +305,15 @@ inline void ChangeEndiannessStore(LiftoffAssembler* assm, LiftoffRegister src,
int LiftoffAssembler::PrepareStackFrame() { int LiftoffAssembler::PrepareStackFrame() {
int offset = pc_offset(); int offset = pc_offset();
// When constant that represents size of stack frame can't be represented // When the frame size is bigger than 4KB, we need seven instructions for
// as 16bit we need three instructions to add it to sp, so we reserve space // stack checking, so we reserve space for this case.
// for this case.
addiu(sp, sp, 0); addiu(sp, sp, 0);
nop(); nop();
nop(); nop();
nop();
nop();
nop();
nop();
return offset; return offset;
} }
...@@ -338,8 +342,8 @@ void LiftoffAssembler::PrepareTailCall(int num_callee_stack_params, ...@@ -338,8 +342,8 @@ void LiftoffAssembler::PrepareTailCall(int num_callee_stack_params,
void LiftoffAssembler::AlignFrameSize() {} void LiftoffAssembler::AlignFrameSize() {}
void LiftoffAssembler::PatchPrepareStackFrame(int offset, void LiftoffAssembler::PatchPrepareStackFrame(
SafepointTableBuilder*) { int offset, SafepointTableBuilder* safepoint_table_builder) {
// The frame_size includes the frame marker. The frame marker has already been // The frame_size includes the frame marker. The frame marker has already been
// pushed on the stack though, so we don't need to allocate memory for it // pushed on the stack though, so we don't need to allocate memory for it
// anymore. // anymore.
...@@ -351,10 +355,66 @@ void LiftoffAssembler::PatchPrepareStackFrame(int offset, ...@@ -351,10 +355,66 @@ void LiftoffAssembler::PatchPrepareStackFrame(int offset,
TurboAssembler patching_assembler( TurboAssembler patching_assembler(
nullptr, AssemblerOptions{}, CodeObjectRequired::kNo, nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
ExternalAssemblerBuffer(buffer_start_ + offset, kAvailableSpace)); ExternalAssemblerBuffer(buffer_start_ + offset, kAvailableSpace));
// If bytes can be represented as 16bit, addiu will be generated and two
// nops will stay untouched. Otherwise, lui-ori sequence will load it to if (V8_LIKELY(frame_size < 4 * KB)) {
// register and, as third instruction, addu will be generated. // This is the standard case for small frames: just subtract from SP and be
patching_assembler.Addu(sp, sp, Operand(-frame_size)); // done with it.
patching_assembler.Addu(sp, sp, Operand(-frame_size));
return;
}
// 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 {__ Addu(sp, 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()}).
int imm32 = pc_offset() - offset - 3 * kInstrSize;
patching_assembler.BranchLong(imm32);
// 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) {
Register stack_limit = kScratchReg;
Lw(stack_limit,
FieldMemOperand(kWasmInstanceRegister,
WasmInstanceObject::kRealStackLimitAddressOffset));
Lw(stack_limit, MemOperand(stack_limit));
Addu(stack_limit, stack_limit, Operand(frame_size));
Branch(&continuation, uge, sp, Operand(stack_limit));
}
// The instance has not been written to the frame yet (because no frame space
// has been allocated), but the runtime call expects it. Hence push it now.
Push(kWasmInstanceRegister);
Call(wasm::WasmCode::kWasmStackOverflow, RelocInfo::WASM_STUB_CALL);
// The call will not return; just define an empty safepoint.
safepoint_table_builder->DefineSafepoint(this);
if (FLAG_debug_code) stop();
bind(&continuation);
// Now allocate the stack space. Note that this might do more than just
// decrementing the SP;
Addu(sp, sp, Operand(-frame_size));
// Jump back to the start of the function, from {pc_offset()} to
// right after the reserved space for the {__ Addu(sp, sp, -framesize)} (which
// is a jump now).
int func_start_offset = offset + 7 * kInstrSize;
imm32 = func_start_offset - pc_offset() - 3 * kInstrSize;
BranchLong(imm32);
} }
void LiftoffAssembler::FinishCode() {} void LiftoffAssembler::FinishCode() {}
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "src/codegen/machine-type.h" #include "src/codegen/machine-type.h"
#include "src/heap/memory-chunk.h" #include "src/heap/memory-chunk.h"
#include "src/wasm/baseline/liftoff-assembler.h" #include "src/wasm/baseline/liftoff-assembler.h"
#include "src/wasm/wasm-objects.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -291,12 +292,15 @@ inline void ChangeEndiannessStore(LiftoffAssembler* assm, LiftoffRegister src, ...@@ -291,12 +292,15 @@ inline void ChangeEndiannessStore(LiftoffAssembler* assm, LiftoffRegister src,
int LiftoffAssembler::PrepareStackFrame() { int LiftoffAssembler::PrepareStackFrame() {
int offset = pc_offset(); int offset = pc_offset();
// When constant that represents size of stack frame can't be represented // When the frame size is bigger than 4KB, we need seven instructions for
// as 16bit we need three instructions to add it to sp, so we reserve space // stack checking, so we reserve space for this case.
// for this case.
daddiu(sp, sp, 0); daddiu(sp, sp, 0);
nop(); nop();
nop(); nop();
nop();
nop();
nop();
nop();
return offset; return offset;
} }
...@@ -325,8 +329,8 @@ void LiftoffAssembler::PrepareTailCall(int num_callee_stack_params, ...@@ -325,8 +329,8 @@ void LiftoffAssembler::PrepareTailCall(int num_callee_stack_params,
void LiftoffAssembler::AlignFrameSize() {} void LiftoffAssembler::AlignFrameSize() {}
void LiftoffAssembler::PatchPrepareStackFrame(int offset, void LiftoffAssembler::PatchPrepareStackFrame(
SafepointTableBuilder*) { int offset, SafepointTableBuilder* safepoint_table_builder) {
// The frame_size includes the frame marker. The frame marker has already been // The frame_size includes the frame marker. The frame marker has already been
// pushed on the stack though, so we don't need to allocate memory for it // pushed on the stack though, so we don't need to allocate memory for it
// anymore. // anymore.
...@@ -338,10 +342,66 @@ void LiftoffAssembler::PatchPrepareStackFrame(int offset, ...@@ -338,10 +342,66 @@ void LiftoffAssembler::PatchPrepareStackFrame(int offset,
TurboAssembler patching_assembler( TurboAssembler patching_assembler(
nullptr, AssemblerOptions{}, CodeObjectRequired::kNo, nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
ExternalAssemblerBuffer(buffer_start_ + offset, kAvailableSpace)); ExternalAssemblerBuffer(buffer_start_ + offset, kAvailableSpace));
// If bytes can be represented as 16bit, daddiu will be generated and two
// nops will stay untouched. Otherwise, lui-ori sequence will load it to if (V8_LIKELY(frame_size < 4 * KB)) {
// register and, as third instruction, daddu will be generated. // This is the standard case for small frames: just subtract from SP and be
patching_assembler.Daddu(sp, sp, Operand(-frame_size)); // done with it.
patching_assembler.Daddu(sp, sp, Operand(-frame_size));
return;
}
// 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 {__ Daddu(sp, sp, -frame_size)} 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()}).
int imm32 = pc_offset() - offset - 3 * kInstrSize;
patching_assembler.BranchLong(imm32);
// 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) {
Register stack_limit = kScratchReg;
Ld(stack_limit,
FieldMemOperand(kWasmInstanceRegister,
WasmInstanceObject::kRealStackLimitAddressOffset));
Ld(stack_limit, MemOperand(stack_limit));
Daddu(stack_limit, stack_limit, Operand(frame_size));
Branch(&continuation, uge, sp, Operand(stack_limit));
}
// The instance has not been written to the frame yet (because no frame space
// has been allocated), but the runtime call expects it. Hence push it now.
Push(kWasmInstanceRegister);
Call(wasm::WasmCode::kWasmStackOverflow, RelocInfo::WASM_STUB_CALL);
// The call will not return; just define an empty safepoint.
safepoint_table_builder->DefineSafepoint(this);
if (FLAG_debug_code) stop();
bind(&continuation);
// Now allocate the stack space. Note that this might do more than just
// decrementing the SP;
Daddu(sp, sp, Operand(-frame_size));
// Jump back to the start of the function, from {pc_offset()} to
// right after the reserved space for the {__ Daddu(sp, sp, -framesize)}
// (which is a Branch now).
int func_start_offset = offset + 7 * kInstrSize;
imm32 = func_start_offset - pc_offset() - 3 * kInstrSize;
BranchLong(imm32);
} }
void LiftoffAssembler::FinishCode() {} 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