1736 lines
72 KiB
Diff
1736 lines
72 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Jakob Gruber <jgruber@chromium.org>
|
|
Date: Thu, 23 Sep 2021 07:26:38 +0200
|
|
Subject: 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}
|
|
|
|
diff --git a/src/api/api.cc b/src/api/api.cc
|
|
index 97b0804562c7cd60791901fa9a8d48ff07c1abbf..f43dfaf5ef10f83416a915952439c0e97656904d 100644
|
|
--- a/src/api/api.cc
|
|
+++ b/src/api/api.cc
|
|
@@ -107,7 +107,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"
|
|
diff --git a/src/codegen/external-reference.cc b/src/codegen/external-reference.cc
|
|
index 2c7748f223c9359079b2c35b55aeaa04810eff85..770653d50ce129452829c43ef5cee3648f2f52aa 100644
|
|
--- a/src/codegen/external-reference.cc
|
|
+++ b/src/codegen/external-reference.cc
|
|
@@ -740,6 +740,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());
|
|
diff --git a/src/codegen/external-reference.h b/src/codegen/external-reference.h
|
|
index cbc3463841332fbd3a9d40f5a1b3d3d1c3d382f7..86deb275f8b179eef7784cb30139b3c9735b7db7 100644
|
|
--- a/src/codegen/external-reference.h
|
|
+++ b/src/codegen/external-reference.h
|
|
@@ -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") \
|
|
diff --git a/src/debug/debug-interface.cc b/src/debug/debug-interface.cc
|
|
index 9c25064572112b649c108a89df3c83b29ac603a0..229e4ca8d8770a698dfb21606b29ac7ac0018dc2 100644
|
|
--- a/src/debug/debug-interface.cc
|
|
+++ b/src/debug/debug-interface.cc
|
|
@@ -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 {
|
|
diff --git a/src/execution/isolate.cc b/src/execution/isolate.cc
|
|
index eaf3096ac6bb05d61d93012d5b7abec503a3259e..051e58263cb66b1ad2dc1b59143d5826e0dfe376 100644
|
|
--- a/src/execution/isolate.cc
|
|
+++ b/src/execution/isolate.cc
|
|
@@ -3591,7 +3591,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);
|
|
diff --git a/src/regexp/arm/regexp-macro-assembler-arm.cc b/src/regexp/arm/regexp-macro-assembler-arm.cc
|
|
index 6c90e00817342a115ec49a21b561a7014e0ce8f8..10766db4cf1d41ad0fee0754c6eaeebe46ace500 100644
|
|
--- a/src/regexp/arm/regexp-macro-assembler-arm.cc
|
|
+++ b/src/regexp/arm/regexp-macro-assembler-arm.cc
|
|
@@ -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,14 +1033,24 @@ void RegExpMacroAssemblerARM::ReadCurrentPositionFromRegister(int reg) {
|
|
__ ldr(current_input_offset(), register_location(reg));
|
|
}
|
|
|
|
+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));
|
|
- __ ldr(r0, MemOperand(frame_pointer(), kStackHighEnd));
|
|
- __ add(backtrack_stackpointer(), backtrack_stackpointer(), Operand(r0));
|
|
+ __ add(backtrack_stackpointer(), backtrack_stackpointer(), r0);
|
|
}
|
|
|
|
-
|
|
void RegExpMacroAssemblerARM::SetCurrentPositionFromEnd(int by) {
|
|
Label after_position;
|
|
__ cmp(current_input_offset(), Operand(-by * char_size()));
|
|
@@ -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() {
|
|
diff --git a/src/regexp/arm/regexp-macro-assembler-arm.h b/src/regexp/arm/regexp-macro-assembler-arm.h
|
|
index a02a4dc2af546e53a89161aad9f3500a51c062f8..a76f9dea70264d79d57ebd6c60b100bc9e0a499d 100644
|
|
--- a/src/regexp/arm/regexp-macro-assembler-arm.h
|
|
+++ b/src/regexp/arm/regexp-macro-assembler-arm.h
|
|
@@ -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_;
|
|
diff --git a/src/regexp/arm64/regexp-macro-assembler-arm64.cc b/src/regexp/arm64/regexp-macro-assembler-arm64.cc
|
|
index 67793ffc411ccb9c32f67fff393e6b77d094b325..6192461fa32879469d56d36fb788b5de33038d77 100644
|
|
--- a/src/regexp/arm64/regexp-macro-assembler-arm64.cc
|
|
+++ b/src/regexp/arm64/regexp-macro-assembler-arm64.cc
|
|
@@ -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,14 +1241,29 @@ void RegExpMacroAssemblerARM64::ReadCurrentPositionFromRegister(int reg) {
|
|
}
|
|
}
|
|
|
|
+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);
|
|
- __ Ldr(x11, MemOperand(frame_pointer(), kStackBase));
|
|
+ __ Mov(x11, ref);
|
|
+ __ Ldr(x11, MemOperand(x11));
|
|
__ Add(backtrack_stackpointer(), x11, Operand(read_from, SXTW));
|
|
}
|
|
|
|
-
|
|
void RegExpMacroAssemblerARM64::SetCurrentPositionFromEnd(int by) {
|
|
Label after_position;
|
|
__ Cmp(current_input_offset(), -by * char_size());
|
|
@@ -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) {
|
|
diff --git a/src/regexp/arm64/regexp-macro-assembler-arm64.h b/src/regexp/arm64/regexp-macro-assembler-arm64.h
|
|
index 80931e3ca42f7d85a3dea067ca203b252a0f78f0..204ee68dc868142693e9959170c71df3f72f97ce 100644
|
|
--- a/src/regexp/arm64/regexp-macro-assembler-arm64.h
|
|
+++ b/src/regexp/arm64/regexp-macro-assembler-arm64.h
|
|
@@ -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_;
|
|
diff --git a/src/regexp/ia32/regexp-macro-assembler-ia32.cc b/src/regexp/ia32/regexp-macro-assembler-ia32.cc
|
|
index 6af1d02eed36af46a7e0d819d006361f51411ba6..51d63b2531e2bc85fb115de23d7b6a6f40b36f11 100644
|
|
--- a/src/regexp/ia32/regexp-macro-assembler-ia32.cc
|
|
+++ b/src/regexp/ia32/regexp-macro-assembler-ia32.cc
|
|
@@ -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) {
|
|
diff --git a/src/regexp/ia32/regexp-macro-assembler-ia32.h b/src/regexp/ia32/regexp-macro-assembler-ia32.h
|
|
index 93fb2c9aba32ab48e335c41dd9dbe0bac94d73ed..861795da900d91111386e4f8e660f7f94ea46a33 100644
|
|
--- a/src/regexp/ia32/regexp-macro-assembler-ia32.h
|
|
+++ b/src/regexp/ia32/regexp-macro-assembler-ia32.h
|
|
@@ -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_;
|
|
diff --git a/src/regexp/regexp-macro-assembler.cc b/src/regexp/regexp-macro-assembler.cc
|
|
index 891079b357a0191e20ea31b68df3a123b4d1b8d2..1f5875afb8850da4136da6633d5b0ad9f52803e3 100644
|
|
--- a/src/regexp/regexp-macro-assembler.cc
|
|
+++ b/src/regexp/regexp-macro-assembler.cc
|
|
@@ -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
|
|
diff --git a/src/regexp/regexp-macro-assembler.h b/src/regexp/regexp-macro-assembler.h
|
|
index 9bd9ba615e32ad9e34e246a7c4788fdf89375476..af3cc2f5caab97a3a57589d0c54011c07269227f 100644
|
|
--- a/src/regexp/regexp-macro-assembler.h
|
|
+++ b/src/regexp/regexp-macro-assembler.h
|
|
@@ -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,
|
|
diff --git a/src/regexp/regexp-stack.cc b/src/regexp/regexp-stack.cc
|
|
index 6d73b7c03d63f9358f54453aba3c52ec5da29c3b..9c403eed089c890df0098875763da62bce15fd64 100644
|
|
--- a/src/regexp/regexp-stack.cc
|
|
+++ b/src/regexp/regexp-stack.cc
|
|
@@ -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;
|
|
diff --git a/src/regexp/regexp-stack.h b/src/regexp/regexp-stack.h
|
|
index adca683ff890c82d7cfdf9e10c7a5d7e78d2d650..d52ca3e1d079d8953a753e2db1e835f214b980f3 100644
|
|
--- a/src/regexp/regexp-stack.h
|
|
+++ b/src/regexp/regexp-stack.h
|
|
@@ -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;
|
|
};
|
|
|
|
diff --git a/src/regexp/x64/regexp-macro-assembler-x64.cc b/src/regexp/x64/regexp-macro-assembler-x64.cc
|
|
index 6f0cb53e8f52e4cad5997993b7fb00f2d8b8127a..abcbed18aaa9bdc4a497962714bffde74d581173 100644
|
|
--- a/src/regexp/x64/regexp-macro-assembler-x64.cc
|
|
+++ b/src/regexp/x64/regexp-macro-assembler-x64.cc
|
|
@@ -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,13 +1132,25 @@ void RegExpMacroAssemblerX64::ReadPositionFromRegister(Register dst, int reg) {
|
|
__ movq(dst, register_location(reg));
|
|
}
|
|
|
|
+// 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) {
|
|
- __ movq(backtrack_stackpointer(), register_location(reg));
|
|
- __ addq(backtrack_stackpointer(), Operand(rbp, kStackHighEnd));
|
|
+ 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;
|
|
__ cmpq(rdi, Immediate(-by * char_size()));
|
|
@@ -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() {
|
|
diff --git a/src/regexp/x64/regexp-macro-assembler-x64.h b/src/regexp/x64/regexp-macro-assembler-x64.h
|
|
index c3a3cb90f2a9d865057af80801e2a95bbb873140..74a3c95b06c771078ab03e6787e5912315421bb2 100644
|
|
--- a/src/regexp/x64/regexp-macro-assembler-x64.h
|
|
+++ b/src/regexp/x64/regexp-macro-assembler-x64.h
|
|
@@ -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_;
|
|
diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status
|
|
index 9c28520ed56998173c105b9d8a2ca3c4489b916e..e092d3b74c79d5dfc2bbf056881164c16ebb7cda 100644
|
|
--- a/test/cctest/cctest.status
|
|
+++ b/test/cctest/cctest.status
|
|
@@ -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
|
|
|
|
##############################################################################
|