[regexp] Allow reentrant irregexp execution

.. by reusing the regexp stack from potentially multiple nested
irregexp activations.

To do this, we now maintain a stack pointer in RegExpStack. This stack
pointer is synchronized at all boundaries between generated irregexp
code and the outside world, i.e. when entering or returning from
irregexp code, and when calling into C functions such as GrowStack.

Fixed: v8:11382
Change-Id: I5ed27630c1a64ebf3afb9ddf80fb60ea067c0c40
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3162604
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Reviewed-by: Patrick Thier <pthier@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Auto-Submit: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77013}
This commit is contained in:
Jakob Gruber 2021-09-23 07:26:38 +02:00 committed by V8 LUCI CQ
parent e262e1cb4a
commit bba7c09aad
18 changed files with 544 additions and 287 deletions

View File

@ -108,7 +108,6 @@
#include "src/profiler/heap-snapshot-generator-inl.h"
#include "src/profiler/profile-generator-inl.h"
#include "src/profiler/tick-sample.h"
#include "src/regexp/regexp-stack.h"
#include "src/regexp/regexp-utils.h"
#include "src/runtime/runtime.h"
#include "src/snapshot/code-serializer.h"

View File

@ -761,6 +761,11 @@ ExternalReference ExternalReference::address_of_regexp_stack_memory_top_address(
isolate->regexp_stack()->memory_top_address_address());
}
ExternalReference ExternalReference::address_of_regexp_stack_stack_pointer(
Isolate* isolate) {
return ExternalReference(isolate->regexp_stack()->stack_pointer_address());
}
ExternalReference ExternalReference::javascript_execution_assert(
Isolate* isolate) {
return ExternalReference(isolate->javascript_execution_assert_address());

View File

@ -72,6 +72,8 @@ class StatsCounter;
"RegExpStack::limit_address_address()") \
V(address_of_regexp_stack_memory_top_address, \
"RegExpStack::memory_top_address_address()") \
V(address_of_regexp_stack_stack_pointer, \
"RegExpStack::stack_pointer_address()") \
V(address_of_static_offsets_vector, "OffsetsVector::static_offsets_vector") \
V(thread_in_wasm_flag_address_address, \
"Isolate::thread_in_wasm_flag_address_address") \

View File

@ -17,7 +17,6 @@
#include "src/objects/js-generator-inl.h"
#include "src/objects/stack-frame-info-inl.h"
#include "src/profiler/heap-profiler.h"
#include "src/regexp/regexp-stack.h"
#include "src/strings/string-builder-inl.h"
#if V8_ENABLE_WEBASSEMBLY
@ -304,10 +303,7 @@ void SetTerminateOnResume(Isolate* v8_isolate) {
bool CanBreakProgram(Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_DO_NOT_USE(isolate);
// We cannot break a program if we are currently running a regexp.
// TODO(yangguo): fix this exception.
return !isolate->regexp_stack()->is_in_use() &&
isolate->debug()->AllFramesOnStackAreBlackboxed();
return isolate->debug()->AllFramesOnStackAreBlackboxed();
}
Isolate* Script::GetIsolate() const {

View File

@ -3610,7 +3610,6 @@ bool Isolate::Init(SnapshotData* startup_snapshot_data,
store_stub_cache_ = new StubCache(this);
materialized_object_store_ = new MaterializedObjectStore(this);
regexp_stack_ = new RegExpStack();
regexp_stack_->isolate_ = this;
date_cache_ = new DateCache();
heap_profiler_ = new HeapProfiler(heap());
interpreter_ = new interpreter::Interpreter(this);

View File

@ -6,15 +6,13 @@
#include "src/regexp/arm/regexp-macro-assembler-arm.h"
#include "src/codegen/assembler-inl.h"
#include "src/codegen/arm/assembler-arm-inl.h"
#include "src/codegen/macro-assembler.h"
#include "src/heap/factory.h"
#include "src/logging/log.h"
#include "src/objects/objects-inl.h"
#include "src/regexp/regexp-macro-assembler.h"
#include "src/objects/code-inl.h"
#include "src/regexp/regexp-stack.h"
#include "src/snapshot/embedded/embedded-data.h"
#include "src/strings/unicode.h"
namespace v8 {
namespace internal {
@ -102,6 +100,7 @@ RegExpMacroAssemblerARM::RegExpMacroAssemblerARM(Isolate* isolate, Zone* zone,
: NativeRegExpMacroAssembler(isolate, zone),
masm_(new MacroAssembler(isolate, CodeObjectRequired::kYes,
NewAssemblerBuffer(kRegExpCodeSize))),
no_root_array_scope_(masm_),
mode_(mode),
num_registers_(registers_to_save),
num_saved_registers_(registers_to_save),
@ -110,8 +109,6 @@ RegExpMacroAssemblerARM::RegExpMacroAssemblerARM(Isolate* isolate, Zone* zone,
success_label_(),
backtrack_label_(),
exit_label_() {
masm_->set_root_array_available(false);
DCHECK_EQ(0, registers_to_save % 2);
__ jmp(&entry_label_); // We'll write the entry code later.
__ bind(&start_label_); // And then continue from here.
@ -619,6 +616,42 @@ void RegExpMacroAssemblerARM::Fail() {
__ jmp(&exit_label_);
}
void RegExpMacroAssemblerARM::LoadRegExpStackPointerFromMemory(Register dst) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_stack_pointer(isolate());
__ mov(dst, Operand(ref));
__ ldr(dst, MemOperand(dst));
}
void RegExpMacroAssemblerARM::StoreRegExpStackPointerToMemory(
Register src, Register scratch) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_stack_pointer(isolate());
__ mov(scratch, Operand(ref));
__ str(src, MemOperand(scratch));
}
void RegExpMacroAssemblerARM::PushRegExpBasePointer(Register scratch1,
Register scratch2) {
LoadRegExpStackPointerFromMemory(scratch1);
ExternalReference ref =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ mov(scratch2, Operand(ref));
__ ldr(scratch2, MemOperand(scratch2));
__ sub(scratch2, scratch1, scratch2);
__ str(scratch2, MemOperand(frame_pointer(), kRegExpStackBasePointer));
}
void RegExpMacroAssemblerARM::PopRegExpBasePointer(Register scratch1,
Register scratch2) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ ldr(scratch1, MemOperand(frame_pointer(), kRegExpStackBasePointer));
__ mov(scratch2, Operand(ref));
__ ldr(scratch2, MemOperand(scratch2));
__ add(scratch1, scratch1, scratch2);
StoreRegExpStackPointerToMemory(scratch1, scratch2);
}
Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
Label return_r0;
@ -654,6 +687,13 @@ Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
__ push(r0); // Make room for "string start - 1" constant.
STATIC_ASSERT(kBacktrackCount == kStringStartMinusOne - kSystemPointerSize);
__ push(r0); // The backtrack counter.
STATIC_ASSERT(kRegExpStackBasePointer ==
kBacktrackCount - kSystemPointerSize);
__ push(r0); // The regexp stack base ptr.
// Store the regexp base pointer - we'll later restore it / write it to
// memory when returning from this irregexp code object.
PushRegExpBasePointer(r0, r1);
// Check if we have space on the stack for registers.
Label stack_limit_hit;
@ -736,7 +776,7 @@ Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
}
// Initialize backtrack stack pointer.
__ ldr(backtrack_stackpointer(), MemOperand(frame_pointer(), kStackHighEnd));
LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
__ jmp(&start_label_);
@ -834,6 +874,10 @@ Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
}
__ bind(&return_r0);
// Restore the original regexp stack pointer value (effectively, pop the
// stored base pointer).
PopRegExpBasePointer(r1, r2);
// Skip sp past regexp registers and local variables..
__ mov(sp, frame_pointer());
// Restore registers r4..r11 and return (restoring lr to pc).
@ -851,12 +895,16 @@ Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
if (check_preempt_label_.is_linked()) {
SafeCallTarget(&check_preempt_label_);
StoreRegExpStackPointerToMemory(backtrack_stackpointer(), r1);
CallCheckStackGuardState();
__ cmp(r0, Operand::Zero());
// If returning non-zero, we should end execution with the given
// result as return value.
__ b(ne, &return_r0);
LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
// String might have moved: Reload end of string from frame.
__ ldr(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
SafeReturn();
@ -867,17 +915,18 @@ Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
SafeCallTarget(&stack_overflow_label_);
// Reached if the backtrack-stack limit has been hit.
// Call GrowStack(backtrack_stackpointer(), &stack_base)
static const int num_arguments = 3;
__ PrepareCallCFunction(num_arguments);
__ mov(r0, backtrack_stackpointer());
__ add(r1, frame_pointer(), Operand(kStackHighEnd));
__ mov(r2, Operand(ExternalReference::isolate_address(isolate())));
// Call GrowStack(isolate).
StoreRegExpStackPointerToMemory(backtrack_stackpointer(), r1);
static constexpr int kNumArguments = 1;
__ PrepareCallCFunction(kNumArguments);
__ mov(r0, Operand(ExternalReference::isolate_address(isolate())));
ExternalReference grow_stack =
ExternalReference::re_grow_stack(isolate());
__ CallCFunction(grow_stack, num_arguments);
// If return nullptr, we have failed to grow the stack, and
// must exit with a stack-overflow exception.
__ CallCFunction(grow_stack, kNumArguments);
// If nullptr is returned, we have failed to grow the stack, and must exit
// with a stack-overflow exception.
__ cmp(r0, Operand::Zero());
__ b(eq, &exit_with_exception);
// Otherwise use return value as new stack pointer.
@ -984,13 +1033,23 @@ void RegExpMacroAssemblerARM::ReadCurrentPositionFromRegister(int reg) {
__ ldr(current_input_offset(), register_location(reg));
}
void RegExpMacroAssemblerARM::ReadStackPointerFromRegister(int reg) {
__ ldr(backtrack_stackpointer(), register_location(reg));
__ ldr(r0, MemOperand(frame_pointer(), kStackHighEnd));
__ add(backtrack_stackpointer(), backtrack_stackpointer(), Operand(r0));
void RegExpMacroAssemblerARM::WriteStackPointerToRegister(int reg) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ mov(r1, Operand(ref));
__ ldr(r1, MemOperand(r1));
__ sub(r0, backtrack_stackpointer(), r1);
__ str(r0, register_location(reg));
}
void RegExpMacroAssemblerARM::ReadStackPointerFromRegister(int reg) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ mov(r0, Operand(ref));
__ ldr(r0, MemOperand(r0));
__ ldr(backtrack_stackpointer(), register_location(reg));
__ add(backtrack_stackpointer(), backtrack_stackpointer(), r0);
}
void RegExpMacroAssemblerARM::SetCurrentPositionFromEnd(int by) {
Label after_position;
@ -1037,14 +1096,6 @@ void RegExpMacroAssemblerARM::ClearRegisters(int reg_from, int reg_to) {
}
}
void RegExpMacroAssemblerARM::WriteStackPointerToRegister(int reg) {
__ ldr(r1, MemOperand(frame_pointer(), kStackHighEnd));
__ sub(r0, backtrack_stackpointer(), r1);
__ str(r0, register_location(reg));
}
// Private methods:
void RegExpMacroAssemblerARM::CallCheckStackGuardState() {

View File

@ -5,8 +5,6 @@
#ifndef V8_REGEXP_ARM_REGEXP_MACRO_ASSEMBLER_ARM_H_
#define V8_REGEXP_ARM_REGEXP_MACRO_ASSEMBLER_ARM_H_
#include "src/base/strings.h"
#include "src/codegen/arm/assembler-arm.h"
#include "src/codegen/macro-assembler.h"
#include "src/regexp/regexp-macro-assembler.h"
@ -115,8 +113,14 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM
static const int kSuccessfulCaptures = kInputString - kPointerSize;
static const int kStringStartMinusOne = kSuccessfulCaptures - kPointerSize;
static const int kBacktrackCount = kStringStartMinusOne - kSystemPointerSize;
// Stores the initial value of the regexp stack pointer in a
// position-independent representation (in case the regexp stack grows and
// thus moves).
static const int kRegExpStackBasePointer =
kBacktrackCount - kSystemPointerSize;
// First register address. Following registers are below it on the stack.
static const int kRegisterZero = kBacktrackCount - kSystemPointerSize;
static const int kRegisterZero = kRegExpStackBasePointer - kSystemPointerSize;
// Initial size of code buffer.
static const int kRegExpCodeSize = 1024;
@ -129,7 +133,6 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM
// Check whether we are exceeding the stack limit on the backtrack stack.
void CheckStackLimit();
// Generate a call to CheckStackGuardState.
void CallCheckStackGuardState();
@ -138,27 +141,27 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM
// Register holding the current input position as negative offset from
// the end of the string.
inline Register current_input_offset() { return r6; }
static constexpr Register current_input_offset() { return r6; }
// The register containing the current character after LoadCurrentCharacter.
inline Register current_character() { return r7; }
static constexpr Register current_character() { return r7; }
// Register holding address of the end of the input string.
inline Register end_of_input_address() { return r10; }
static constexpr Register end_of_input_address() { return r10; }
// Register holding the frame address. Local variables, parameters and
// regexp registers are addressed relative to this.
inline Register frame_pointer() { return fp; }
static constexpr Register frame_pointer() { return fp; }
// The register containing the backtrack stack top. Provides a meaningful
// name to the register.
inline Register backtrack_stackpointer() { return r8; }
static constexpr Register backtrack_stackpointer() { return r8; }
// Register holding pointer to the current code object.
inline Register code_pointer() { return r5; }
static constexpr Register code_pointer() { return r5; }
// Byte size of chars in the string to match (decided by the Mode argument)
inline int char_size() { return static_cast<int>(mode_); }
inline int char_size() const { return static_cast<int>(mode_); }
// Equivalent to a conditional branch to the label, unless the label
// is nullptr, in which case it is a conditional Backtrack.
@ -178,19 +181,25 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM
// and increments it by a word size.
inline void Pop(Register target);
void LoadRegExpStackPointerFromMemory(Register dst);
void StoreRegExpStackPointerToMemory(Register src, Register scratch);
void PushRegExpBasePointer(Register scratch1, Register scratch2);
void PopRegExpBasePointer(Register scratch1, Register scratch2);
Isolate* isolate() const { return masm_->isolate(); }
MacroAssembler* masm_;
MacroAssembler* const masm_;
const NoRootArrayScope no_root_array_scope_;
// Which mode to generate code for (Latin1 or UC16).
Mode mode_;
const Mode mode_;
// One greater than maximal register index actually used.
int num_registers_;
// Number of registers to output at the end (the saved registers
// are always 0..num_saved_registers_-1)
int num_saved_registers_;
const int num_saved_registers_;
// Labels used internally.
Label entry_label_;

View File

@ -113,6 +113,7 @@ RegExpMacroAssemblerARM64::RegExpMacroAssemblerARM64(Isolate* isolate,
: NativeRegExpMacroAssembler(isolate, zone),
masm_(new MacroAssembler(isolate, CodeObjectRequired::kYes,
NewAssemblerBuffer(kRegExpCodeSize))),
no_root_array_scope_(masm_),
mode_(mode),
num_registers_(registers_to_save),
num_saved_registers_(registers_to_save),
@ -121,8 +122,6 @@ RegExpMacroAssemblerARM64::RegExpMacroAssemblerARM64(Isolate* isolate,
success_label_(),
backtrack_label_(),
exit_label_() {
masm_->set_root_array_available(false);
DCHECK_EQ(0, registers_to_save % 2);
// We can cache at most 16 W registers in x0-x7.
STATIC_ASSERT(kNumCachedRegisters <= 16);
@ -699,6 +698,42 @@ void RegExpMacroAssemblerARM64::Fail() {
__ B(&exit_label_);
}
void RegExpMacroAssemblerARM64::LoadRegExpStackPointerFromMemory(Register dst) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_stack_pointer(isolate());
__ Mov(dst, ref);
__ Ldr(dst, MemOperand(dst));
}
void RegExpMacroAssemblerARM64::StoreRegExpStackPointerToMemory(
Register src, Register scratch) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_stack_pointer(isolate());
__ Mov(scratch, ref);
__ Str(src, MemOperand(scratch));
}
void RegExpMacroAssemblerARM64::PushRegExpBasePointer(Register scratch1,
Register scratch2) {
LoadRegExpStackPointerFromMemory(scratch1);
ExternalReference ref =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ Mov(scratch2, ref);
__ Ldr(scratch2, MemOperand(scratch2));
__ Sub(scratch2, scratch1, scratch2);
__ Str(scratch2, MemOperand(frame_pointer(), kRegExpStackBasePointer));
}
void RegExpMacroAssemblerARM64::PopRegExpBasePointer(Register scratch1,
Register scratch2) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ Ldr(scratch1, MemOperand(frame_pointer(), kRegExpStackBasePointer));
__ Mov(scratch2, ref);
__ Ldr(scratch2, MemOperand(scratch2));
__ Add(scratch1, scratch1, scratch2);
StoreRegExpStackPointerToMemory(scratch1, scratch2);
}
Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
Label return_w0;
@ -744,22 +779,27 @@ Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
__ Mov(input_end(), x3);
__ Mov(output_array(), x4);
// Set the number of registers we will need to allocate, that is:
// - kSuccessCounter / success_counter (X register)
// - kBacktrackCount (X register)
// - (num_registers_ - kNumCachedRegisters) (W registers)
int num_wreg_to_allocate = num_registers_ - kNumCachedRegisters;
// Do not allocate registers on the stack if they can all be cached.
if (num_wreg_to_allocate < 0) { num_wreg_to_allocate = 0; }
// Make room for the success_counter and kBacktrackCount. Each X (64-bit)
// register is equivalent to two W (32-bit) registers.
num_wreg_to_allocate += 2 + 2;
// Make sure the stack alignment will be respected.
int alignment = masm_->ActivationFrameAlignment();
const int alignment = masm_->ActivationFrameAlignment();
DCHECK_EQ(alignment % 16, 0);
int align_mask = (alignment / kWRegSize) - 1;
num_wreg_to_allocate = (num_wreg_to_allocate + align_mask) & ~align_mask;
const int align_mask = (alignment / kWRegSize) - 1;
// Make room for stack locals.
static constexpr int kWRegPerXReg = kXRegSize / kWRegSize;
DCHECK_EQ(kNumberOfStackLocals * kWRegPerXReg,
((kNumberOfStackLocals * kWRegPerXReg) + align_mask) & ~align_mask);
__ Claim(kNumberOfStackLocals * kWRegPerXReg);
// Store the regexp base pointer - we'll later restore it / write it to
// memory when returning from this irregexp code object.
PushRegExpBasePointer(x10, x11);
// Set the number of registers we will need to allocate, that is:
// - (num_registers_ - kNumCachedRegisters) (W registers)
const int num_stack_registers =
std::max(0, num_registers_ - kNumCachedRegisters);
const int num_wreg_to_allocate =
(num_stack_registers + align_mask) & ~align_mask;
// Check if we have space on the stack.
Label stack_limit_hit;
@ -839,9 +879,9 @@ Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
}
// Initialize backtrack stack pointer.
__ Ldr(backtrack_stackpointer(), MemOperand(frame_pointer(), kStackBase));
LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
// Execute
// Execute.
__ B(&start_label_);
if (backtrack_label_.is_linked()) {
@ -1013,7 +1053,7 @@ Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
}
if (exit_label_.is_linked()) {
// Exit and return w0
// Exit and return w0.
__ Bind(&exit_label_);
if (global()) {
__ Ldr(w0, MemOperand(frame_pointer(), kSuccessCounter));
@ -1021,8 +1061,11 @@ Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
}
__ Bind(&return_w0);
// Restore the original regexp stack pointer value (effectively, pop the
// stored base pointer).
PopRegExpBasePointer(x10, x11);
// Set stack pointer back to first register to retain
// Set stack pointer back to first register to retain.
__ Mov(sp, fp);
__ Pop<TurboAssembler::kAuthLR>(fp, lr);
@ -1039,6 +1082,9 @@ Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
if (check_preempt_label_.is_linked()) {
__ Bind(&check_preempt_label_);
StoreRegExpStackPointerToMemory(backtrack_stackpointer(), x10);
SaveLinkRegister();
// The cached registers need to be retained.
__ PushCPURegList(cached_registers);
@ -1048,26 +1094,30 @@ Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
__ Cbnz(w0, &return_w0);
// Reset the cached registers.
__ PopCPURegList(cached_registers);
LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
RestoreLinkRegister();
__ Ret();
}
if (stack_overflow_label_.is_linked()) {
__ Bind(&stack_overflow_label_);
StoreRegExpStackPointerToMemory(backtrack_stackpointer(), x10);
SaveLinkRegister();
// The cached registers need to be retained.
__ PushCPURegList(cached_registers);
// Call GrowStack(backtrack_stackpointer(), &stack_base)
__ Mov(x2, ExternalReference::isolate_address(isolate()));
__ Add(x1, frame_pointer(), kStackBase);
__ Mov(x0, backtrack_stackpointer());
ExternalReference grow_stack =
ExternalReference::re_grow_stack(isolate());
__ CallCFunction(grow_stack, 3);
// If return nullptr, we have failed to grow the stack, and
// must exit with a stack-overflow exception.
// Returning from the regexp code restores the stack (sp <- fp)
// so we don't need to drop the link register from it before exiting.
// Call GrowStack(isolate)
static constexpr int kNumArguments = 1;
__ Mov(x0, ExternalReference::isolate_address(isolate()));
__ CallCFunction(ExternalReference::re_grow_stack(isolate()),
kNumArguments);
// If return nullptr, we have failed to grow the stack, and must exit with
// a stack-overflow exception. Returning from the regexp code restores the
// stack (sp <- fp) so we don't need to drop the link register from it
// before exiting.
__ Cbz(w0, &exit_with_exception);
// Otherwise use return value as new stack pointer.
__ Mov(backtrack_stackpointer(), x0);
@ -1191,13 +1241,28 @@ void RegExpMacroAssemblerARM64::ReadCurrentPositionFromRegister(int reg) {
}
}
void RegExpMacroAssemblerARM64::ReadStackPointerFromRegister(int reg) {
Register read_from = GetRegister(reg, w10);
__ Ldr(x11, MemOperand(frame_pointer(), kStackBase));
__ Add(backtrack_stackpointer(), x11, Operand(read_from, SXTW));
void RegExpMacroAssemblerARM64::WriteStackPointerToRegister(int reg) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ Mov(x10, ref);
__ Ldr(x10, MemOperand(x10));
__ Sub(x10, backtrack_stackpointer(), x10);
if (FLAG_debug_code) {
__ Cmp(x10, Operand(w10, SXTW));
// The stack offset needs to fit in a W register.
__ Check(eq, AbortReason::kOffsetOutOfRange);
}
StoreRegister(reg, w10);
}
void RegExpMacroAssemblerARM64::ReadStackPointerFromRegister(int reg) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
Register read_from = GetRegister(reg, w10);
__ Mov(x11, ref);
__ Ldr(x11, MemOperand(x11));
__ Add(backtrack_stackpointer(), x11, Operand(read_from, SXTW));
}
void RegExpMacroAssemblerARM64::SetCurrentPositionFromEnd(int by) {
Label after_position;
@ -1300,19 +1365,6 @@ void RegExpMacroAssemblerARM64::ClearRegisters(int reg_from, int reg_to) {
}
}
void RegExpMacroAssemblerARM64::WriteStackPointerToRegister(int reg) {
__ Ldr(x10, MemOperand(frame_pointer(), kStackBase));
__ Sub(x10, backtrack_stackpointer(), x10);
if (FLAG_debug_code) {
__ Cmp(x10, Operand(w10, SXTW));
// The stack offset needs to fit in a W register.
__ Check(eq, AbortReason::kOffsetOutOfRange);
}
StoreRegister(reg, w10);
}
// Helper function for reading a value out of a stack frame.
template <typename T>
static T& frame_entry(Address re_frame, int frame_offset) {

View File

@ -110,18 +110,28 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM64
// Below the frame pointer.
// Register parameters stored by setup code.
static const int kDirectCall = -kSystemPointerSize;
static const int kStackBase = kDirectCall - kSystemPointerSize;
static const int kOutputSize = kStackBase - kSystemPointerSize;
static const int kStackHighEnd = kDirectCall - kSystemPointerSize;
static const int kOutputSize = kStackHighEnd - kSystemPointerSize;
static const int kInput = kOutputSize - kSystemPointerSize;
// When adding local variables remember to push space for them in
// the frame in GetCode.
static const int kSuccessCounter = kInput - kSystemPointerSize;
static const int kBacktrackCount = kSuccessCounter - kSystemPointerSize;
// Stores the initial value of the regexp stack pointer in a
// position-independent representation (in case the regexp stack grows and
// thus moves).
static const int kRegExpStackBasePointer =
kBacktrackCount - kSystemPointerSize;
// A padding slot to preserve alignment.
static const int kStackLocalPadding =
kRegExpStackBasePointer - kSystemPointerSize;
static constexpr int kNumberOfStackLocals = 4;
// First position register address on the stack. Following positions are
// below it. A position is a 32 bit value.
static const int kFirstRegisterOnStack = kBacktrackCount - kWRegSize;
static const int kFirstRegisterOnStack = kStackLocalPadding - kWRegSize;
// A capture is a 64 bit value holding two position.
static const int kFirstCaptureOnStack = kBacktrackCount - kXRegSize;
static const int kFirstCaptureOnStack = kStackLocalPadding - kXRegSize;
// Initial size of code buffer.
static const int kRegExpCodeSize = 1024;
@ -152,43 +162,43 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM64
// Register holding the current input position as negative offset from
// the end of the string.
Register current_input_offset() { return w21; }
static constexpr Register current_input_offset() { return w21; }
// The register containing the current character after LoadCurrentCharacter.
Register current_character() { return w22; }
static constexpr Register current_character() { return w22; }
// Register holding address of the end of the input string.
Register input_end() { return x25; }
static constexpr Register input_end() { return x25; }
// Register holding address of the start of the input string.
Register input_start() { return x26; }
static constexpr Register input_start() { return x26; }
// Register holding the offset from the start of the string where we should
// start matching.
Register start_offset() { return w27; }
static constexpr Register start_offset() { return w27; }
// Pointer to the output array's first element.
Register output_array() { return x28; }
static constexpr Register output_array() { return x28; }
// Register holding the frame address. Local variables, parameters and
// regexp registers are addressed relative to this.
Register frame_pointer() { return fp; }
static constexpr Register frame_pointer() { return fp; }
// The register containing the backtrack stack top. Provides a meaningful
// name to the register.
Register backtrack_stackpointer() { return x23; }
static constexpr Register backtrack_stackpointer() { return x23; }
// Register holding pointer to the current code object.
Register code_pointer() { return x20; }
static constexpr Register code_pointer() { return x20; }
// Register holding the value used for clearing capture registers.
Register string_start_minus_one() { return w24; }
static constexpr Register string_start_minus_one() { return w24; }
// The top 32 bit of this register is used to store this value
// twice. This is used for clearing more than one register at a time.
Register twice_non_position_value() { return x24; }
static constexpr Register twice_non_position_value() { return x24; }
// Byte size of chars in the string to match (decided by the Mode argument)
int char_size() { return static_cast<int>(mode_); }
int char_size() const { return static_cast<int>(mode_); }
// Equivalent to a conditional branch to the label, unless the label
// is nullptr, in which case it is a conditional Backtrack.
@ -254,19 +264,25 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM64
// This assumes that the state of the register is not STACKED.
inline Register GetCachedRegister(int register_index);
void LoadRegExpStackPointerFromMemory(Register dst);
void StoreRegExpStackPointerToMemory(Register src, Register scratch);
void PushRegExpBasePointer(Register scratch1, Register scratch2);
void PopRegExpBasePointer(Register scratch1, Register scratch2);
Isolate* isolate() const { return masm_->isolate(); }
MacroAssembler* masm_;
MacroAssembler* const masm_;
const NoRootArrayScope no_root_array_scope_;
// Which mode to generate code for (LATIN1 or UC16).
Mode mode_;
const Mode mode_;
// One greater than maximal register index actually used.
int num_registers_;
// Number of registers to output at the end (the saved registers
// are always 0..num_saved_registers_-1)
int num_saved_registers_;
const int num_saved_registers_;
// Labels used internally.
Label entry_label_;

View File

@ -90,6 +90,7 @@ RegExpMacroAssemblerIA32::RegExpMacroAssemblerIA32(Isolate* isolate, Zone* zone,
: NativeRegExpMacroAssembler(isolate, zone),
masm_(new MacroAssembler(isolate, CodeObjectRequired::kYes,
NewAssemblerBuffer(kRegExpCodeSize))),
no_root_array_scope_(masm_),
mode_(mode),
num_registers_(registers_to_save),
num_saved_registers_(registers_to_save),
@ -98,9 +99,6 @@ RegExpMacroAssemblerIA32::RegExpMacroAssemblerIA32(Isolate* isolate, Zone* zone,
success_label_(),
backtrack_label_(),
exit_label_() {
// Irregexp code clobbers ebx and spills/restores it at all boundaries.
masm_->set_root_array_available(false);
DCHECK_EQ(0, registers_to_save % 2);
__ jmp(&entry_label_); // We'll write the entry code later.
__ bind(&start_label_); // And then continue from here.
@ -655,6 +653,38 @@ void RegExpMacroAssemblerIA32::Fail() {
__ jmp(&exit_label_);
}
void RegExpMacroAssemblerIA32::LoadRegExpStackPointerFromMemory(Register dst) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_stack_pointer(isolate());
__ mov(dst, __ ExternalReferenceAsOperand(ref, dst));
}
void RegExpMacroAssemblerIA32::StoreRegExpStackPointerToMemory(
Register src, Register scratch) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_stack_pointer(isolate());
__ mov(__ ExternalReferenceAsOperand(ref, scratch), src);
}
void RegExpMacroAssemblerIA32::PushRegExpBasePointer(Register scratch1,
Register scratch2) {
LoadRegExpStackPointerFromMemory(scratch1);
ExternalReference ref =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ mov(scratch2, __ ExternalReferenceAsOperand(ref, scratch2));
__ sub(scratch1, scratch2);
__ mov(Operand(ebp, kRegExpStackBasePointer), scratch1);
}
void RegExpMacroAssemblerIA32::PopRegExpBasePointer(Register scratch1,
Register scratch2) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ mov(scratch1, Operand(ebp, kRegExpStackBasePointer));
__ mov(scratch2, __ ExternalReferenceAsOperand(ref, scratch2));
__ add(scratch1, scratch2);
StoreRegExpStackPointerToMemory(scratch1, scratch2);
}
Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
Label return_eax;
@ -676,14 +706,23 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
__ push(esi);
__ push(edi);
__ push(ebx); // Callee-save on MacOS.
STATIC_ASSERT(kLastCalleeSaveRegister == kBackup_ebx);
STATIC_ASSERT(kSuccessfulCaptures == kBackup_ebx - kSystemPointerSize);
STATIC_ASSERT(kSuccessfulCaptures ==
kLastCalleeSaveRegister - kSystemPointerSize);
__ push(Immediate(0)); // Number of successful matches in a global regexp.
STATIC_ASSERT(kStringStartMinusOne ==
kSuccessfulCaptures - kSystemPointerSize);
__ push(Immediate(0)); // Make room for "string start - 1" constant.
STATIC_ASSERT(kBacktrackCount == kStringStartMinusOne - kSystemPointerSize);
__ push(Immediate(0)); // The backtrack counter.
STATIC_ASSERT(kRegExpStackBasePointer ==
kBacktrackCount - kSystemPointerSize);
__ push(Immediate(0)); // The regexp stack base ptr.
// Store the regexp base pointer - we'll later restore it / write it to
// memory when returning from this irregexp code object.
PushRegExpBasePointer(ecx, eax);
// Check if we have space on the stack for registers.
Label stack_limit_hit;
@ -769,7 +808,7 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
}
// Initialize backtrack stack pointer.
__ mov(backtrack_stackpointer(), Operand(ebp, kStackHighEnd));
LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
__ jmp(&start_label_);
@ -855,8 +894,12 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
}
__ bind(&return_eax);
// Restore the original regexp stack pointer value (effectively, pop the
// stored base pointer).
PopRegExpBasePointer(ecx, ebx);
// Skip esp past regexp registers.
__ lea(esp, Operand(ebp, kBackup_ebx));
__ lea(esp, Operand(ebp, kLastCalleeSaveRegister));
// Restore callee-save registers.
__ pop(ebx);
__ pop(edi);
@ -877,7 +920,8 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
if (check_preempt_label_.is_linked()) {
SafeCallTarget(&check_preempt_label_);
__ push(backtrack_stackpointer());
StoreRegExpStackPointerToMemory(backtrack_stackpointer(), edi);
__ push(edi);
CallCheckStackGuardState(ebx);
@ -887,7 +931,9 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
__ j(not_zero, &return_eax);
__ pop(edi);
__ pop(backtrack_stackpointer());
LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
// String might have moved: Reload esi from frame.
__ mov(esi, Operand(ebp, kInputEnd));
SafeReturn();
@ -898,21 +944,19 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
SafeCallTarget(&stack_overflow_label_);
// Reached if the backtrack-stack limit has been hit.
// Save registers before calling C function
// Save registers before calling C function.
__ push(esi);
__ push(edi);
// Call GrowStack(backtrack_stackpointer())
static const int num_arguments = 3;
__ PrepareCallCFunction(num_arguments, ebx);
__ mov(Operand(esp, 2 * kSystemPointerSize),
StoreRegExpStackPointerToMemory(backtrack_stackpointer(), edi);
// Call GrowStack(isolate).
static const int kNumArguments = 1;
__ PrepareCallCFunction(kNumArguments, ebx);
__ mov(Operand(esp, 0 * kSystemPointerSize),
Immediate(ExternalReference::isolate_address(isolate())));
__ lea(eax, Operand(ebp, kStackHighEnd));
__ mov(Operand(esp, 1 * kSystemPointerSize), eax);
__ mov(Operand(esp, 0 * kSystemPointerSize), backtrack_stackpointer());
ExternalReference grow_stack =
ExternalReference::re_grow_stack(isolate());
__ CallCFunction(grow_stack, num_arguments);
__ CallCFunction(ExternalReference::re_grow_stack(isolate()),
kNumArguments);
// If return nullptr, we have failed to grow the stack, and
// must exit with a stack-overflow exception.
__ or_(eax, eax);
@ -1019,10 +1063,21 @@ void RegExpMacroAssemblerIA32::ReadCurrentPositionFromRegister(int reg) {
__ mov(edi, register_location(reg));
}
void RegExpMacroAssemblerIA32::WriteStackPointerToRegister(int reg) {
ExternalReference stack_top_address =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ mov(eax, __ ExternalReferenceAsOperand(stack_top_address, eax));
__ sub(eax, backtrack_stackpointer());
__ mov(register_location(reg), eax);
}
void RegExpMacroAssemblerIA32::ReadStackPointerFromRegister(int reg) {
__ mov(backtrack_stackpointer(), register_location(reg));
__ add(backtrack_stackpointer(), Operand(ebp, kStackHighEnd));
ExternalReference stack_top_address =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ mov(backtrack_stackpointer(),
__ ExternalReferenceAsOperand(stack_top_address,
backtrack_stackpointer()));
__ sub(backtrack_stackpointer(), register_location(reg));
}
void RegExpMacroAssemblerIA32::SetCurrentPositionFromEnd(int by) {
@ -1069,14 +1124,6 @@ void RegExpMacroAssemblerIA32::ClearRegisters(int reg_from, int reg_to) {
}
}
void RegExpMacroAssemblerIA32::WriteStackPointerToRegister(int reg) {
__ mov(eax, backtrack_stackpointer());
__ sub(eax, Operand(ebp, kStackHighEnd));
__ mov(register_location(reg), eax);
}
// Private methods:
void RegExpMacroAssemblerIA32::CallCheckStackGuardState(Register scratch) {

View File

@ -114,12 +114,20 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerIA32
static const int kBackup_esi = kFramePointer - kSystemPointerSize;
static const int kBackup_edi = kBackup_esi - kSystemPointerSize;
static const int kBackup_ebx = kBackup_edi - kSystemPointerSize;
static const int kSuccessfulCaptures = kBackup_ebx - kSystemPointerSize;
static const int kLastCalleeSaveRegister = kBackup_ebx;
static const int kSuccessfulCaptures =
kLastCalleeSaveRegister - kSystemPointerSize;
static const int kStringStartMinusOne =
kSuccessfulCaptures - kSystemPointerSize;
static const int kBacktrackCount = kStringStartMinusOne - kSystemPointerSize;
// Stores the initial value of the regexp stack pointer in a
// position-independent representation (in case the regexp stack grows and
// thus moves).
static const int kRegExpStackBasePointer =
kBacktrackCount - kSystemPointerSize;
// First register address. Following registers are below it on the stack.
static const int kRegisterZero = kBacktrackCount - kSystemPointerSize;
static const int kRegisterZero = kRegExpStackBasePointer - kSystemPointerSize;
// Initial size of code buffer.
static const int kRegExpCodeSize = 1024;
@ -137,14 +145,14 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerIA32
Operand register_location(int register_index);
// The register containing the current character after LoadCurrentCharacter.
inline Register current_character() { return edx; }
static constexpr Register current_character() { return edx; }
// The register containing the backtrack stack top. Provides a meaningful
// name to the register.
inline Register backtrack_stackpointer() { return ecx; }
static constexpr Register backtrack_stackpointer() { return ecx; }
// Byte size of chars in the string to match (decided by the Mode argument)
inline int char_size() { return static_cast<int>(mode_); }
inline int char_size() const { return static_cast<int>(mode_); }
// Equivalent to a conditional branch to the label, unless the label
// is nullptr, in which case it is a conditional Backtrack.
@ -168,19 +176,25 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerIA32
// (ecx) and increments it by a word size.
inline void Pop(Register target);
void LoadRegExpStackPointerFromMemory(Register dst);
void StoreRegExpStackPointerToMemory(Register src, Register scratch);
void PushRegExpBasePointer(Register scratch1, Register scratch2);
void PopRegExpBasePointer(Register scratch1, Register scratch2);
Isolate* isolate() const { return masm_->isolate(); }
MacroAssembler* masm_;
MacroAssembler* const masm_;
const NoRootArrayScope no_root_array_scope_;
// Which mode to generate code for (LATIN1 or UC16).
Mode mode_;
const Mode mode_;
// One greater than maximal register index actually used.
int num_registers_;
// Number of registers to output at the end (the saved registers
// are always 0..num_saved_registers_-1)
int num_saved_registers_;
// are always 0..num_saved_registers_-1).
const int num_saved_registers_;
// Labels used internally.
Label entry_label_;

View File

@ -308,7 +308,7 @@ int NativeRegExpMacroAssembler::Execute(
int* output, int output_size, Isolate* isolate, JSRegExp regexp) {
// Ensure that the minimum stack has been allocated.
RegExpStackScope stack_scope(isolate);
Address stack_base = stack_scope.stack()->stack_base();
Address stack_base = stack_scope.stack()->memory_top();
bool is_one_byte = String::IsOneByteRepresentationUnderneath(input);
Code code = FromCodeT(CodeT::cast(regexp.Code(is_one_byte)));
@ -382,22 +382,23 @@ const byte NativeRegExpMacroAssembler::word_character_map[] = {
};
// clang-format on
Address NativeRegExpMacroAssembler::GrowStack(Address stack_pointer,
Address* stack_base,
Isolate* isolate) {
Address NativeRegExpMacroAssembler::GrowStack(Isolate* isolate) {
DisallowGarbageCollection no_gc;
RegExpStack* regexp_stack = isolate->regexp_stack();
size_t size = regexp_stack->stack_capacity();
Address old_stack_base = regexp_stack->stack_base();
DCHECK(old_stack_base == *stack_base);
DCHECK(stack_pointer <= old_stack_base);
DCHECK(static_cast<size_t>(old_stack_base - stack_pointer) <= size);
Address new_stack_base = regexp_stack->EnsureCapacity(size * 2);
if (new_stack_base == kNullAddress) {
return kNullAddress;
}
*stack_base = new_stack_base;
intptr_t stack_content_size = old_stack_base - stack_pointer;
return new_stack_base - stack_content_size;
const size_t old_size = regexp_stack->memory_size();
#ifdef DEBUG
const Address old_stack_top = regexp_stack->memory_top();
const Address old_stack_pointer = regexp_stack->stack_pointer();
CHECK_LE(old_stack_pointer, old_stack_top);
CHECK_LE(static_cast<size_t>(old_stack_top - old_stack_pointer), old_size);
#endif // DEBUG
Address new_stack_base = regexp_stack->EnsureCapacity(old_size * 2);
if (new_stack_base == kNullAddress) return kNullAddress;
return regexp_stack->stack_pointer();
}
} // namespace internal

View File

@ -281,13 +281,11 @@ class NativeRegExpMacroAssembler: public RegExpMacroAssembler {
int* offsets_vector, int offsets_vector_length,
int previous_index, Isolate* isolate);
// Called from RegExp if the backtrack stack limit is hit.
// Tries to expand the stack. Returns the new stack-pointer if
// successful, and updates the stack_top address, or returns 0 if unable
// to grow the stack.
// Called from RegExp if the backtrack stack limit is hit. Tries to expand
// the stack. Returns the new stack-pointer if successful, or returns 0 if
// unable to grow the stack.
// This function must not trigger a garbage collection.
static Address GrowStack(Address stack_pointer, Address* stack_top,
Isolate* isolate);
static Address GrowStack(Isolate* isolate);
static int CheckStackGuardState(Isolate* isolate, int start_index,
RegExp::CallOrigin call_origin,

View File

@ -11,23 +11,17 @@ namespace v8 {
namespace internal {
RegExpStackScope::RegExpStackScope(Isolate* isolate)
: regexp_stack_(isolate->regexp_stack()) {
: regexp_stack_(isolate->regexp_stack()),
old_sp_top_delta_(regexp_stack_->sp_top_delta()) {
DCHECK(regexp_stack_->IsValid());
// Irregexp is not reentrant in several ways; in particular, the
// RegExpStackScope is not reentrant since the destructor frees allocated
// memory. Protect against reentrancy here.
CHECK(!regexp_stack_->is_in_use());
regexp_stack_->set_is_in_use(true);
}
RegExpStackScope::~RegExpStackScope() {
// Reset the buffer if it has grown.
regexp_stack_->Reset();
DCHECK(!regexp_stack_->is_in_use());
CHECK_EQ(old_sp_top_delta_, regexp_stack_->sp_top_delta());
regexp_stack_->ResetIfEmpty();
}
RegExpStack::RegExpStack() : thread_local_(this), isolate_(nullptr) {}
RegExpStack::RegExpStack() : thread_local_(this) {}
RegExpStack::~RegExpStack() { thread_local_.FreeAndInvalidate(); }
@ -52,18 +46,16 @@ char* RegExpStack::RestoreStack(char* from) {
return from + kThreadLocalSize;
}
void RegExpStack::Reset() { thread_local_.ResetToStaticStack(this); }
void RegExpStack::ThreadLocal::ResetToStaticStack(RegExpStack* regexp_stack) {
if (owns_memory_) DeleteArray(memory_);
memory_ = regexp_stack->static_stack_;
memory_top_ = regexp_stack->static_stack_ + kStaticStackSize;
memory_size_ = kStaticStackSize;
stack_pointer_ = memory_top_;
limit_ = reinterpret_cast<Address>(regexp_stack->static_stack_) +
kStackLimitSlack * kSystemPointerSize;
owns_memory_ = false;
is_in_use_ = false;
}
void RegExpStack::ThreadLocal::FreeAndInvalidate() {
@ -74,6 +66,7 @@ void RegExpStack::ThreadLocal::FreeAndInvalidate() {
memory_ = nullptr;
memory_top_ = nullptr;
memory_size_ = 0;
stack_pointer_ = nullptr;
limit_ = kMemoryTop;
}
@ -88,9 +81,11 @@ Address RegExpStack::EnsureCapacity(size_t size) {
thread_local_.memory_, thread_local_.memory_size_);
if (thread_local_.owns_memory_) DeleteArray(thread_local_.memory_);
}
ptrdiff_t delta = sp_top_delta();
thread_local_.memory_ = new_memory;
thread_local_.memory_top_ = new_memory + size;
thread_local_.memory_size_ = size;
thread_local_.stack_pointer_ = thread_local_.memory_top_ + delta;
thread_local_.limit_ = reinterpret_cast<Address>(new_memory) +
kStackLimitSlack * kSystemPointerSize;
thread_local_.owns_memory_ = true;

View File

@ -16,10 +16,7 @@ class RegExpStack;
// Maintains a per-v8thread stack area that can be used by irregexp
// implementation for its backtracking stack.
// Since there is only one stack area, the Irregexp implementation is not
// re-entrant. I.e., no regular expressions may be executed in the same thread
// during a preempted Irregexp execution.
class V8_NODISCARD RegExpStackScope {
class V8_NODISCARD RegExpStackScope final {
public:
// Create and delete an instance to control the life-time of a growing stack.
@ -32,46 +29,45 @@ class V8_NODISCARD RegExpStackScope {
RegExpStack* stack() const { return regexp_stack_; }
private:
RegExpStack* regexp_stack_;
RegExpStack* const regexp_stack_;
const ptrdiff_t old_sp_top_delta_;
};
class RegExpStack {
class RegExpStack final {
public:
RegExpStack();
~RegExpStack();
RegExpStack(const RegExpStack&) = delete;
RegExpStack& operator=(const RegExpStack&) = delete;
// Number of allocated locations on the stack below the limit.
// No sequence of pushes must be longer that this without doing a stack-limit
// check.
// Number of allocated locations on the stack below the limit. No sequence of
// pushes must be longer than this without doing a stack-limit check.
static constexpr int kStackLimitSlack = 32;
// Gives the top of the memory used as stack.
Address stack_base() {
Address memory_top() const {
DCHECK_NE(0, thread_local_.memory_size_);
DCHECK_EQ(thread_local_.memory_top_,
thread_local_.memory_ + thread_local_.memory_size_);
return reinterpret_cast<Address>(thread_local_.memory_top_);
}
// The total size of the memory allocated for the stack.
size_t stack_capacity() { return thread_local_.memory_size_; }
Address stack_pointer() const {
return reinterpret_cast<Address>(thread_local_.stack_pointer_);
}
size_t memory_size() const { return thread_local_.memory_size_; }
// If the stack pointer gets below the limit, we should react and
// either grow the stack or report an out-of-stack exception.
// There is only a limited number of locations below the stack limit,
// so users of the stack should check the stack limit during any
// sequence of pushes longer that this.
Address* limit_address_address() { return &(thread_local_.limit_); }
Address* limit_address_address() { return &thread_local_.limit_; }
// Ensures that there is a memory area with at least the specified size.
// If passing zero, the default/minimum size buffer is allocated.
Address EnsureCapacity(size_t size);
bool is_in_use() const { return thread_local_.is_in_use_; }
void set_is_in_use(bool v) { thread_local_.is_in_use_ = v; }
// Thread local archiving.
static constexpr int ArchiveSpacePerThread() {
return static_cast<int>(kThreadLocalSize);
@ -103,44 +99,59 @@ class RegExpStack {
STATIC_ASSERT(kStaticStackSize <= kMaximumStackSize);
// Structure holding the allocated memory, size and limit.
// Structure holding the allocated memory, size and limit. Thread switching
// archives and restores this struct.
struct ThreadLocal {
explicit ThreadLocal(RegExpStack* regexp_stack) {
ResetToStaticStack(regexp_stack);
}
// If memory_size_ > 0 then memory_ and memory_top_ must be non-nullptr
// and memory_top_ = memory_ + memory_size_
// If memory_size_ > 0 then
// - memory_, memory_top_, stack_pointer_ must be non-nullptr
// - memory_top_ = memory_ + memory_size_
// - memory_ <= stack_pointer_ <= memory_top_
byte* memory_ = nullptr;
byte* memory_top_ = nullptr;
size_t memory_size_ = 0;
byte* stack_pointer_ = nullptr;
Address limit_ = kNullAddress;
bool owns_memory_ = false; // Whether memory_ is owned and must be freed.
bool is_in_use_ = false; // To guard against reentrancy.
void ResetToStaticStack(RegExpStack* regexp_stack);
void ResetToStaticStackIfEmpty(RegExpStack* regexp_stack) {
if (stack_pointer_ == memory_top_) ResetToStaticStack(regexp_stack);
}
void FreeAndInvalidate();
};
static constexpr size_t kThreadLocalSize = sizeof(ThreadLocal);
// Address of top of memory used as stack.
Address memory_top_address_address() {
return reinterpret_cast<Address>(&thread_local_.memory_top_);
}
// Resets the buffer if it has grown beyond the default/minimum size.
// After this, the buffer is either the default size, or it is empty, so
// you have to call EnsureCapacity before using it again.
void Reset();
Address stack_pointer_address() {
return reinterpret_cast<Address>(&thread_local_.stack_pointer_);
}
// A position-independent representation of the stack pointer.
ptrdiff_t sp_top_delta() const {
ptrdiff_t result =
reinterpret_cast<intptr_t>(thread_local_.stack_pointer_) -
reinterpret_cast<intptr_t>(thread_local_.memory_top_);
DCHECK_LE(result, 0);
return result;
}
// Resets the buffer if it has grown beyond the default/minimum size and is
// empty.
void ResetIfEmpty() { thread_local_.ResetToStaticStackIfEmpty(this); }
// Whether the ThreadLocal storage has been invalidated.
bool IsValid() const { return thread_local_.memory_ != nullptr; }
ThreadLocal thread_local_;
Isolate* isolate_;
friend class ExternalReference;
friend class Isolate;
friend class RegExpStackScope;
};

View File

@ -6,13 +6,13 @@
#include "src/regexp/x64/regexp-macro-assembler-x64.h"
#include "src/codegen/code-desc.h"
#include "src/codegen/macro-assembler.h"
#include "src/heap/factory.h"
#include "src/logging/log.h"
#include "src/objects/objects-inl.h"
#include "src/objects/code-inl.h"
#include "src/regexp/regexp-macro-assembler.h"
#include "src/regexp/regexp-stack.h"
#include "src/strings/unicode.h"
namespace v8 {
namespace internal {
@ -664,31 +664,64 @@ void RegExpMacroAssemblerX64::Fail() {
__ jmp(&exit_label_);
}
void RegExpMacroAssemblerX64::LoadRegExpStackPointerFromMemory(Register dst) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_stack_pointer(isolate());
__ movq(dst, __ ExternalReferenceAsOperand(ref, dst));
}
void RegExpMacroAssemblerX64::StoreRegExpStackPointerToMemory(
Register src, Register scratch) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_stack_pointer(isolate());
__ movq(__ ExternalReferenceAsOperand(ref, scratch), src);
}
void RegExpMacroAssemblerX64::PushRegExpBasePointer(Register scratch1,
Register scratch2) {
LoadRegExpStackPointerFromMemory(scratch1);
ExternalReference ref =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ movq(scratch2, __ ExternalReferenceAsOperand(ref, scratch2));
__ subq(scratch1, scratch2);
__ movq(Operand(rbp, kRegExpStackBasePointer), scratch1);
}
void RegExpMacroAssemblerX64::PopRegExpBasePointer(Register scratch1,
Register scratch2) {
ExternalReference ref =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ movq(scratch1, Operand(rbp, kRegExpStackBasePointer));
__ movq(scratch2, __ ExternalReferenceAsOperand(ref, scratch2));
__ addq(scratch1, scratch2);
StoreRegExpStackPointerToMemory(scratch1, scratch2);
}
Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
Label return_rax;
// Finalize code - write the entry point code now we know how many
// registers we need.
// Entry code:
// Finalize code - write the entry point code now we know how many registers
// we need.
__ bind(&entry_label_);
// Tell the system that we have a stack frame. Because the type is MANUAL, no
// is generated.
// Tell the system that we have a stack frame. Because the type is MANUAL, no
// physical frame is generated.
FrameScope scope(&masm_, StackFrame::MANUAL);
// Actually emit code to start a new stack frame.
__ pushq(rbp);
__ movq(rbp, rsp);
// Save parameters and callee-save registers. Order here should correspond
// to order of kBackup_ebx etc.
#ifdef V8_TARGET_OS_WIN
// MSVC passes arguments in rcx, rdx, r8, r9, with backing stack slots.
// Store register parameters in pre-allocated stack slots,
__ movq(Operand(rbp, kInputString), rcx);
__ movq(Operand(rbp, kStartIndex), rdx); // Passed as int32 in edx.
__ movq(Operand(rbp, kInputStart), r8);
__ movq(Operand(rbp, kInputEnd), r9);
// Callee-save on Win64.
// Store register parameters in pre-allocated stack slots.
__ movq(Operand(rbp, kInputString), arg_reg_1);
__ movq(Operand(rbp, kStartIndex), arg_reg_2); // Passed as int32 in edx.
__ movq(Operand(rbp, kInputStart), arg_reg_3);
__ movq(Operand(rbp, kInputEnd), arg_reg_4);
STATIC_ASSERT(kNumCalleeSaveRegisters == 3);
__ pushq(rsi);
__ pushq(rdi);
__ pushq(rbx);
@ -701,14 +734,15 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
DCHECK_EQ(kInputEnd, -4 * kSystemPointerSize);
DCHECK_EQ(kRegisterOutput, -5 * kSystemPointerSize);
DCHECK_EQ(kNumOutputRegisters, -6 * kSystemPointerSize);
__ pushq(rdi);
__ pushq(rsi);
__ pushq(rdx);
__ pushq(rcx);
__ pushq(arg_reg_1);
__ pushq(arg_reg_2);
__ pushq(arg_reg_3);
__ pushq(arg_reg_4);
__ pushq(r8);
__ pushq(r9);
__ pushq(rbx); // Callee-save
STATIC_ASSERT(kNumCalleeSaveRegisters == 1);
__ pushq(rbx);
#endif
STATIC_ASSERT(kSuccessfulCaptures ==
@ -719,6 +753,13 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
__ Push(Immediate(0)); // Make room for "string start - 1" constant.
STATIC_ASSERT(kBacktrackCount == kStringStartMinusOne - kSystemPointerSize);
__ Push(Immediate(0)); // The backtrack counter.
STATIC_ASSERT(kRegExpStackBasePointer ==
kBacktrackCount - kSystemPointerSize);
__ Push(Immediate(0)); // The regexp stack base ptr.
// Store the regexp base pointer - we'll later restore it / write it to
// memory when returning from this irregexp code object.
PushRegExpBasePointer(rcx, kScratchRegister);
// Check if we have space on the stack for registers.
Label stack_limit_hit;
@ -808,7 +849,9 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
}
// Initialize backtrack stack pointer.
__ movq(backtrack_stackpointer(), Operand(rbp, kStackHighEnd));
// TODO(jgruber): Remove the kStackHighEnd parameter (and others like
// kIsolate).
LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
__ jmp(&start_label_);
@ -894,19 +937,26 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
}
__ bind(&return_rax);
// Restore the original regexp stack pointer value (effectively, pop the
// stored base pointer).
PopRegExpBasePointer(rcx, kScratchRegister);
#ifdef V8_TARGET_OS_WIN
// Restore callee save registers.
__ leaq(rsp, Operand(rbp, kLastCalleeSaveRegister));
STATIC_ASSERT(kNumCalleeSaveRegisters == 3);
__ popq(rbx);
__ popq(rdi);
__ popq(rsi);
// Stack now at rbp.
#else
// Restore callee save register.
STATIC_ASSERT(kNumCalleeSaveRegisters == 1);
__ movq(rbx, Operand(rbp, kBackup_rbx));
// Skip rsp to rbp.
__ movq(rsp, rbp);
#endif
// Exit function frame, restore previous one.
__ popq(rbp);
__ ret(0);
@ -923,9 +973,10 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
if (check_preempt_label_.is_linked()) {
SafeCallTarget(&check_preempt_label_);
__ pushq(backtrack_stackpointer());
__ pushq(rdi);
StoreRegExpStackPointerToMemory(backtrack_stackpointer(), kScratchRegister);
CallCheckStackGuardState();
__ testq(rax, rax);
// If returning non-zero, we should end execution with the given
@ -935,7 +986,9 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
// Restore registers.
__ Move(code_object_pointer(), masm_.CodeObject());
__ popq(rdi);
__ popq(backtrack_stackpointer());
LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
// String might have moved: Reload esi from frame.
__ movq(rsi, Operand(rbp, kInputEnd));
SafeReturn();
@ -953,25 +1006,19 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
__ pushq(rdi);
#endif
// Call GrowStack(backtrack_stackpointer())
static const int num_arguments = 3;
__ PrepareCallCFunction(num_arguments);
#ifdef V8_TARGET_OS_WIN
// Microsoft passes parameters in rcx, rdx, r8.
// First argument, backtrack stackpointer, is already in rcx.
__ leaq(rdx, Operand(rbp, kStackHighEnd)); // Second argument
__ LoadAddress(r8, ExternalReference::isolate_address(isolate()));
#else
// AMD64 ABI passes parameters in rdi, rsi, rdx.
__ movq(rdi, backtrack_stackpointer()); // First argument.
__ leaq(rsi, Operand(rbp, kStackHighEnd)); // Second argument.
__ LoadAddress(rdx, ExternalReference::isolate_address(isolate()));
#endif
// Call GrowStack(isolate).
StoreRegExpStackPointerToMemory(backtrack_stackpointer(), kScratchRegister);
static constexpr int kNumArguments = 1;
__ PrepareCallCFunction(kNumArguments);
__ LoadAddress(arg_reg_1, ExternalReference::isolate_address(isolate()));
ExternalReference grow_stack =
ExternalReference::re_grow_stack(isolate());
__ CallCFunction(grow_stack, num_arguments);
// If return nullptr, we have failed to grow the stack, and
// must exit with a stack-overflow exception.
__ CallCFunction(grow_stack, kNumArguments);
// If nullptr is returned, we have failed to grow the stack, and must exit
// with a stack-overflow exception.
__ testq(rax, rax);
__ j(equal, &exit_with_exception);
// Otherwise use return value as new stack pointer.
@ -1085,12 +1132,24 @@ void RegExpMacroAssemblerX64::ReadPositionFromRegister(Register dst, int reg) {
__ movq(dst, register_location(reg));
}
void RegExpMacroAssemblerX64::ReadStackPointerFromRegister(int reg) {
__ movq(backtrack_stackpointer(), register_location(reg));
__ addq(backtrack_stackpointer(), Operand(rbp, kStackHighEnd));
// Preserves a position-independent representation of the stack pointer in reg:
// reg = top - sp.
void RegExpMacroAssemblerX64::WriteStackPointerToRegister(int reg) {
ExternalReference stack_top_address =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ movq(rax, __ ExternalReferenceAsOperand(stack_top_address, rax));
__ subq(rax, backtrack_stackpointer());
__ movq(register_location(reg), rax);
}
void RegExpMacroAssemblerX64::ReadStackPointerFromRegister(int reg) {
ExternalReference stack_top_address =
ExternalReference::address_of_regexp_stack_memory_top_address(isolate());
__ movq(backtrack_stackpointer(),
__ ExternalReferenceAsOperand(stack_top_address,
backtrack_stackpointer()));
__ subq(backtrack_stackpointer(), register_location(reg));
}
void RegExpMacroAssemblerX64::SetCurrentPositionFromEnd(int by) {
Label after_position;
@ -1136,14 +1195,6 @@ void RegExpMacroAssemblerX64::ClearRegisters(int reg_from, int reg_to) {
}
}
void RegExpMacroAssemblerX64::WriteStackPointerToRegister(int reg) {
__ movq(rax, backtrack_stackpointer());
__ subq(rax, Operand(rbp, kStackHighEnd));
__ movq(register_location(reg), rax);
}
// Private methods:
void RegExpMacroAssemblerX64::CallCheckStackGuardState() {

View File

@ -5,9 +5,7 @@
#ifndef V8_REGEXP_X64_REGEXP_MACRO_ASSEMBLER_X64_H_
#define V8_REGEXP_X64_REGEXP_MACRO_ASSEMBLER_X64_H_
#include "src/base/strings.h"
#include "src/codegen/macro-assembler.h"
#include "src/codegen/x64/assembler-x64.h"
#include "src/regexp/regexp-macro-assembler.h"
#include "src/zone/zone-chunk-list.h"
@ -133,18 +131,17 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerX64
static const int kIsolate = kDirectCall + kSystemPointerSize;
#endif
// We push callee-save registers that we use after the frame pointer (and
// after the parameters).
#ifdef V8_TARGET_OS_WIN
// Microsoft calling convention has three callee-saved registers
// (that we are using). We push these after the frame pointer.
static const int kBackup_rsi = kFramePointer - kSystemPointerSize;
static const int kBackup_rdi = kBackup_rsi - kSystemPointerSize;
static const int kBackup_rbx = kBackup_rdi - kSystemPointerSize;
static const int kNumCalleeSaveRegisters = 3;
static const int kLastCalleeSaveRegister = kBackup_rbx;
#else
// AMD64 Calling Convention has only one callee-save register that
// we use. We push this after the frame pointer (and after the
// parameters).
static const int kBackup_rbx = kNumOutputRegisters - kSystemPointerSize;
static const int kNumCalleeSaveRegisters = 1;
static const int kLastCalleeSaveRegister = kBackup_rbx;
#endif
@ -155,9 +152,14 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerX64
static const int kStringStartMinusOne =
kSuccessfulCaptures - kSystemPointerSize;
static const int kBacktrackCount = kStringStartMinusOne - kSystemPointerSize;
// Stores the initial value of the regexp stack pointer in a
// position-independent representation (in case the regexp stack grows and
// thus moves).
static const int kRegExpStackBasePointer =
kBacktrackCount - kSystemPointerSize;
// First register address. Following registers are below it on the stack.
static const int kRegisterZero = kBacktrackCount - kSystemPointerSize;
static const int kRegisterZero = kRegExpStackBasePointer - kSystemPointerSize;
// Initial size of code buffer.
static const int kRegExpCodeSize = 1024;
@ -175,14 +177,14 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerX64
Operand register_location(int register_index);
// The register containing the current character after LoadCurrentCharacter.
inline Register current_character() { return rdx; }
static constexpr Register current_character() { return rdx; }
// The register containing the backtrack stack top. Provides a meaningful
// name to the register.
inline Register backtrack_stackpointer() { return rcx; }
static constexpr Register backtrack_stackpointer() { return rcx; }
// The registers containing a self pointer to this code's Code object.
inline Register code_object_pointer() { return r8; }
static constexpr Register code_object_pointer() { return r8; }
// Byte size of chars in the string to match (decided by the Mode argument)
inline int char_size() { return static_cast<int>(mode_); }
@ -224,24 +226,36 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerX64
// Increments the stack pointer (rcx) by a word size.
inline void Drop();
void LoadRegExpStackPointerFromMemory(Register dst);
void StoreRegExpStackPointerToMemory(Register src, Register scratch);
void PushRegExpBasePointer(Register scratch1, Register scratch2);
void PopRegExpBasePointer(Register scratch1, Register scratch2);
inline void ReadPositionFromRegister(Register dst, int reg);
Isolate* isolate() const { return masm_.isolate(); }
MacroAssembler masm_;
NoRootArrayScope no_root_array_scope_;
// On x64, there is no reason to keep the kRootRegister uninitialized; we
// could easily use it by 1. initializing it and 2. storing/restoring it
// as callee-save on entry/exit.
// But: on other platforms, specifically ia32, it would be tricky to enable
// the kRootRegister since it's currently used for other purposes. Thus, for
// consistency, we also keep it uninitialized here.
const NoRootArrayScope no_root_array_scope_;
ZoneChunkList<int> code_relative_fixup_positions_;
// Which mode to generate code for (LATIN1 or UC16).
Mode mode_;
const Mode mode_;
// One greater than maximal register index actually used.
int num_registers_;
// Number of registers to output at the end (the saved registers
// are always 0..num_saved_registers_-1)
int num_saved_registers_;
const int num_saved_registers_;
// Labels used internally.
Label entry_label_;

View File

@ -136,9 +136,6 @@
'test-strings/Traverse': [PASS, HEAVY],
'test-swiss-name-dictionary-csa/DeleteAtBoundaries': [PASS, HEAVY],
'test-swiss-name-dictionary-csa/SameH2': [PASS, HEAVY],
# TODO(v8:11382): Reenable once irregexp is reentrant.
'test-regexp/RegExpInterruptReentrantExecution': [FAIL],
}], # ALWAYS
##############################################################################