Browse Source

fix: crash in v8 due to regexp reentrancy (#31144)

* fix: crash in v8 due to regexp reentrancy

Check failed: !regexp_stack_->is_in_use()

Refs https://bugs.chromium.org/p/chromium/issues/detail?id=1250646
Refs https://bugs.chromium.org/p/v8/issues/detail?id=11382

* chore: update patches

* chore: update patches

Co-authored-by: deepak1556 <[email protected]>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
trop[bot] 3 years ago
parent
commit
45813a0342

+ 3 - 0
patches/v8/.patches

@@ -7,3 +7,6 @@ do_not_export_private_v8_symbols_on_windows.patch
 fix_build_deprecated_attirbute_for_older_msvc_versions.patch
 fix_disable_implies_dcheck_for_node_stream_array_buffers.patch
 cppgc-js_support_eager_traced_value_in_ephemeron_pairs.patch
+regexp_add_a_currently_failing_cctest_for_irregexp_reentrancy.patch
+regexp_allow_reentrant_irregexp_execution.patch
+regexp_remove_the_stack_parameter_from_regexp_matchers.patch

+ 109 - 0
patches/v8/regexp_add_a_currently_failing_cctest_for_irregexp_reentrancy.patch

@@ -0,0 +1,109 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jakob Gruber <[email protected]>
+Date: Mon, 6 Sep 2021 08:29:33 +0200
+Subject: Add a (currently failing) cctest for irregexp reentrancy
+
+The test should be enabled once reentrancy is supported.
+
+Bug: v8:11382
+Change-Id: Ifb90d8a6fd8bf9f05e9ca2405d4e04e013ce7ee3
+Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3138201
+Commit-Queue: Jakob Gruber <[email protected]>
+Auto-Submit: Jakob Gruber <[email protected]>
+Reviewed-by: Patrick Thier <[email protected]>
+Cr-Commit-Position: refs/heads/main@{#76667}
+
+diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status
+index 9b369044754443ccf5ce07c3612f0a928e565ad6..21afa5310647eb67f3fe3fc4f2e0721b4bb4e0f6 100644
+--- a/test/cctest/cctest.status
++++ b/test/cctest/cctest.status
+@@ -136,6 +136,9 @@
+   '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
+ 
+ ##############################################################################
+@@ -666,6 +669,9 @@
+ 
+   # Instruction cache flushing is disabled in jitless mode.
+   'test-icache/*': [SKIP],
++
++  # Tests generated irregexp code.
++  'test-regexp/RegExpInterruptReentrantExecution': [SKIP],
+ }], # lite_mode or variant == jitless
+ 
+ ##############################################################################
+diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
+index 25fba193bbcc41d127d36949e103cb59688bfed7..b21222f14c533cb630946b6066bfe24d1be49f93 100644
+--- a/test/cctest/test-api.cc
++++ b/test/cctest/test-api.cc
+@@ -21658,10 +21658,6 @@ TEST(RegExpInterruptAndMakeSubjectTwoByteExternal) {
+   // experimental engine.
+   i::FLAG_enable_experimental_regexp_engine_on_excessive_backtracks = false;
+   RegExpInterruptTest test;
+-  // We want to be stuck regexp execution, so no fallback to linear-time
+-  // engine.
+-  // TODO(mbid,v8:10765): Find a way to test interrupt support of the
+-  // experimental engine.
+   test.RunTest(RegExpInterruptTest::MakeSubjectTwoByteExternal);
+ }
+ 
+diff --git a/test/cctest/test-regexp.cc b/test/cctest/test-regexp.cc
+index aa24fe3dd230ecc06d9eea2920826dc123979c58..2fb5b3d056f78d3eef3a0a1032ee99df1b2f35c5 100644
+--- a/test/cctest/test-regexp.cc
++++ b/test/cctest/test-regexp.cc
+@@ -2348,6 +2348,50 @@ TEST(UnicodePropertyEscapeCodeSize) {
+   }
+ }
+ 
++namespace {
++
++struct RegExpExecData {
++  i::Isolate* isolate;
++  i::Handle<i::JSRegExp> regexp;
++  i::Handle<i::String> subject;
++};
++
++i::Handle<i::Object> RegExpExec(const RegExpExecData* d) {
++  return i::RegExp::Exec(d->isolate, d->regexp, d->subject, 0,
++                         d->isolate->regexp_last_match_info())
++      .ToHandleChecked();
++}
++
++void ReenterRegExp(v8::Isolate* isolate, void* data) {
++  RegExpExecData* d = static_cast<RegExpExecData*>(data);
++  i::Handle<i::Object> result = RegExpExec(d);
++  CHECK(result->IsNull());
++}
++
++}  // namespace
++
++// Tests reentrant irregexp calls.
++TEST(RegExpInterruptReentrantExecution) {
++  CHECK(!i::FLAG_jitless);
++  i::FLAG_regexp_tier_up = false;  // Enter irregexp, not the interpreter.
++
++  LocalContext context;
++  v8::Isolate* isolate = context->GetIsolate();
++  v8::HandleScope scope(isolate);
++
++  RegExpExecData d;
++  d.isolate = reinterpret_cast<i::Isolate*>(isolate);
++  d.regexp = v8::Utils::OpenHandle(
++      *v8::RegExp::New(context.local(), v8_str("(a*)*x"), v8::RegExp::kNone)
++           .ToLocalChecked());
++  d.subject = v8::Utils::OpenHandle(*v8_str("aaaa"));
++
++  isolate->RequestInterrupt(&ReenterRegExp, &d);
++
++  i::Handle<i::Object> result = RegExpExec(&d);
++  CHECK(result->IsNull());
++}
++
+ #undef CHECK_PARSE_ERROR
+ #undef CHECK_SIMPLE
+ #undef CHECK_MIN_MAX

+ 1736 - 0
patches/v8/regexp_allow_reentrant_irregexp_execution.patch

@@ -0,0 +1,1736 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jakob Gruber <[email protected]>
+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 <[email protected]>
+Reviewed-by: Patrick Thier <[email protected]>
+Commit-Queue: Toon Verwaest <[email protected]>
+Auto-Submit: Jakob Gruber <[email protected]>
+Cr-Commit-Position: refs/heads/main@{#77013}
+
+diff --git a/src/api/api.cc b/src/api/api.cc
+index 041a4eaddf7856e2fd9eb3336285e7edb6774712..1e6dde1aaeab016734c3ab290c3b79a959497d92 100644
+--- a/src/api/api.cc
++++ b/src/api/api.cc
+@@ -99,7 +99,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 e1d8c5d96ef5cc183fe5e479bf1e4b220791100c..bdf0adf415e70c1f893bbbcb2e0e8c1b156e66ff 100644
+--- a/src/codegen/external-reference.cc
++++ b/src/codegen/external-reference.cc
+@@ -738,6 +738,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 5112c5ba73f2da26632488c26053c45ea86b51a4..a46c8b6ab955c9c6c1c873bfe4020d135683589e 100644
+--- a/src/debug/debug-interface.cc
++++ b/src/debug/debug-interface.cc
+@@ -16,7 +16,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
+@@ -303,10 +302,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 8363c52c491e9bc6272696cae38f68b141a3e4a6..f2c6ad5167046f5c0a1141dbc9aed956b88ea143 100644
+--- a/src/execution/isolate.cc
++++ b/src/execution/isolate.cc
+@@ -3626,7 +3626,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 6edb1335760e71442fec98c5d5a5ccbb81a3091e..911744b8b1c6d8a49100d88b53f8d9aedb943e2a 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);
+@@ -700,6 +699,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;
+@@ -745,22 +780,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;
+@@ -840,9 +880,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()) {
+@@ -1014,7 +1054,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));
+@@ -1022,8 +1062,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);
+ 
+@@ -1040,6 +1083,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);
+@@ -1049,26 +1095,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);
+@@ -1192,14 +1242,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());
+@@ -1301,19 +1366,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 5457398f39ab4b36921738bb9c924127eb9dc104..27590fac36cea92f557dc0c149d9cbed6efef8b5 100644
+--- a/src/regexp/regexp-macro-assembler.cc
++++ b/src/regexp/regexp-macro-assembler.cc
+@@ -302,7 +302,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)));
+@@ -376,22 +376,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 31e8b1a37039ff462f32904e2234d9678a6a05bc..71ddde8706fcbf1ee7502380467fd98ffe8d856e 100644
+--- a/src/regexp/regexp-macro-assembler.h
++++ b/src/regexp/regexp-macro-assembler.h
+@@ -280,13 +280,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 21afa5310647eb67f3fe3fc4f2e0721b4bb4e0f6..ea49ec9b4096e986ea5fe64b2b06177855d24f69 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
+ 
+ ##############################################################################

+ 397 - 0
patches/v8/regexp_remove_the_stack_parameter_from_regexp_matchers.patch

@@ -0,0 +1,397 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jakob Gruber <[email protected]>
+Date: Wed, 22 Sep 2021 14:42:48 +0200
+Subject: Remove the `stack` parameter from regexp matchers
+
+The argument is no longer in use.
+
+Bug: v8:11382
+Change-Id: I7febc7fe7ef17ae462c700f0dba3ca1beade3021
+Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3173681
+Commit-Queue: Jakob Gruber <[email protected]>
+Reviewed-by: Patrick Thier <[email protected]>
+Cr-Commit-Position: refs/heads/main@{#77017}
+
+diff --git a/src/builtins/builtins-regexp-gen.cc b/src/builtins/builtins-regexp-gen.cc
+index 535188c567ee3eafee2333a45e9d41c4cf3b4a0b..bb7512fbfa26678110a2a26a717975beddd369b8 100644
+--- a/src/builtins/builtins-regexp-gen.cc
++++ b/src/builtins/builtins-regexp-gen.cc
+@@ -435,8 +435,6 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal(
+   // External constants.
+   TNode<ExternalReference> isolate_address =
+       ExternalConstant(ExternalReference::isolate_address(isolate()));
+-  TNode<ExternalReference> regexp_stack_memory_top_address = ExternalConstant(
+-      ExternalReference::address_of_regexp_stack_memory_top_address(isolate()));
+   TNode<ExternalReference> static_offsets_vector_address = ExternalConstant(
+       ExternalReference::address_of_static_offsets_vector(isolate()));
+ 
+@@ -605,26 +603,18 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal(
+     MachineType arg5_type = type_int32;
+     TNode<Int32T> arg5 = SmiToInt32(register_count);
+ 
+-    // Argument 6: Start (high end) of backtracking stack memory area. This
+-    // argument is ignored in the interpreter.
+-    TNode<RawPtrT> stack_top = UncheckedCast<RawPtrT>(
+-        Load(MachineType::Pointer(), regexp_stack_memory_top_address));
++    // Argument 6: Indicate that this is a direct call from JavaScript.
++    MachineType arg6_type = type_int32;
++    TNode<Int32T> arg6 = Int32Constant(RegExp::CallOrigin::kFromJs);
+ 
+-    MachineType arg6_type = type_ptr;
+-    TNode<RawPtrT> arg6 = stack_top;
++    // Argument 7: Pass current isolate address.
++    MachineType arg7_type = type_ptr;
++    TNode<ExternalReference> arg7 = isolate_address;
+ 
+-    // Argument 7: Indicate that this is a direct call from JavaScript.
+-    MachineType arg7_type = type_int32;
+-    TNode<Int32T> arg7 = Int32Constant(RegExp::CallOrigin::kFromJs);
+-
+-    // Argument 8: Pass current isolate address.
+-    MachineType arg8_type = type_ptr;
+-    TNode<ExternalReference> arg8 = isolate_address;
+-
+-    // Argument 9: Regular expression object. This argument is ignored in native
++    // Argument 8: Regular expression object. This argument is ignored in native
+     // irregexp code.
+-    MachineType arg9_type = type_tagged;
+-    TNode<JSRegExp> arg9 = regexp;
++    MachineType arg8_type = type_tagged;
++    TNode<JSRegExp> arg8 = regexp;
+ 
+     // TODO(v8:11880): avoid roundtrips between cdc and code.
+     TNode<RawPtrT> code_entry = LoadCodeObjectEntry(code);
+@@ -639,8 +629,7 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal(
+             std::make_pair(arg1_type, arg1), std::make_pair(arg2_type, arg2),
+             std::make_pair(arg3_type, arg3), std::make_pair(arg4_type, arg4),
+             std::make_pair(arg5_type, arg5), std::make_pair(arg6_type, arg6),
+-            std::make_pair(arg7_type, arg7), std::make_pair(arg8_type, arg8),
+-            std::make_pair(arg9_type, arg9)));
++            std::make_pair(arg7_type, arg7), std::make_pair(arg8_type, arg8)));
+ 
+     // Check the result.
+     // We expect exactly one result since we force the called regexp to behave
+diff --git a/src/regexp/arm/regexp-macro-assembler-arm.cc b/src/regexp/arm/regexp-macro-assembler-arm.cc
+index 10766db4cf1d41ad0fee0754c6eaeebe46ace500..6e79ffd8adf8417c356bb36e1dbb2d0bfc290858 100644
+--- a/src/regexp/arm/regexp-macro-assembler-arm.cc
++++ b/src/regexp/arm/regexp-macro-assembler-arm.cc
+@@ -38,14 +38,12 @@ namespace internal {
+  * Each call to a public method should retain this convention.
+  *
+  * The stack will have the following structure:
+- *  - fp[56]  Address regexp     (address of the JSRegExp object; unused in
++ *  - fp[52]  Address regexp     (address of the JSRegExp object; unused in
+  *                                native code, passed to match signature of
+  *                                the interpreter)
+- *  - fp[52]  Isolate* isolate   (address of the current isolate)
+- *  - fp[48]  direct_call        (if 1, direct call from JavaScript code,
++ *  - fp[48]  Isolate* isolate   (address of the current isolate)
++ *  - fp[44]  direct_call        (if 1, direct call from JavaScript code,
+  *                                if 0, call through the runtime system).
+- *  - fp[44]  stack_area_base    (high end of the memory area to use as
+- *                                backtracking stack).
+  *  - fp[40]  capture array size (may fit multiple sets of matches)
+  *  - fp[36]  int* capture_array (int[num_saved_registers_], for output).
+  *  --- sp when called ---
+@@ -82,7 +80,6 @@ namespace internal {
+  *              Address end,
+  *              int* capture_output_array,
+  *              int num_capture_registers,
+- *              byte* stack_area_base,
+  *              bool direct_call = false,
+  *              Isolate* isolate,
+  *              Address regexp);
+diff --git a/src/regexp/arm/regexp-macro-assembler-arm.h b/src/regexp/arm/regexp-macro-assembler-arm.h
+index a76f9dea70264d79d57ebd6c60b100bc9e0a499d..5aad6c1d85d574f4db307b6edcdda89ed25d5ca8 100644
+--- a/src/regexp/arm/regexp-macro-assembler-arm.h
++++ b/src/regexp/arm/regexp-macro-assembler-arm.h
+@@ -91,15 +91,13 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM
+   static const int kFramePointer = 0;
+ 
+   // Above the frame pointer - Stored registers and stack passed parameters.
+-  // Register 4..11.
+   static const int kStoredRegisters = kFramePointer;
+   // Return address (stored from link register, read into pc on return).
+   static const int kReturnAddress = kStoredRegisters + 8 * kPointerSize;
+   // Stack parameters placed by caller.
+   static const int kRegisterOutput = kReturnAddress + kPointerSize;
+   static const int kNumOutputRegisters = kRegisterOutput + kPointerSize;
+-  static const int kStackHighEnd = kNumOutputRegisters + kPointerSize;
+-  static const int kDirectCall = kStackHighEnd + kPointerSize;
++  static const int kDirectCall = kNumOutputRegisters + kPointerSize;
+   static const int kIsolate = kDirectCall + kPointerSize;
+ 
+   // Below the frame pointer.
+diff --git a/src/regexp/arm64/regexp-macro-assembler-arm64.cc b/src/regexp/arm64/regexp-macro-assembler-arm64.cc
+index 911744b8b1c6d8a49100d88b53f8d9aedb943e2a..ca21530f99b618e5c46511d06ef93121a7e3c23e 100644
+--- a/src/regexp/arm64/regexp-macro-assembler-arm64.cc
++++ b/src/regexp/arm64/regexp-macro-assembler-arm64.cc
+@@ -66,14 +66,12 @@ namespace internal {
+  *  ^^^^^^^^^ fp ^^^^^^^^^
+  *  - fp[-8]     direct_call        1 => Direct call from JavaScript code.
+  *                                  0 => Call through the runtime system.
+- *  - fp[-16]    stack_base         High end of the memory area to use as
+- *                                  the backtracking stack.
+- *  - fp[-24]    output_size        Output may fit multiple sets of matches.
+- *  - fp[-32]    input              Handle containing the input string.
+- *  - fp[-40]    success_counter
++ *  - fp[-16]    output_size        Output may fit multiple sets of matches.
++ *  - fp[-24]    input              Handle containing the input string.
++ *  - fp[-32]    success_counter
+  *  ^^^^^^^^^^^^^ From here and downwards we store 32 bit values ^^^^^^^^^^^^^
+- *  - fp[-44]    register N         Capture registers initialized with
+- *  - fp[-48]    register N + 1     non_position_value.
++ *  - fp[-40]    register N         Capture registers initialized with
++ *  - fp[-44]    register N + 1     non_position_value.
+  *               ...                The first kNumCachedRegisters (N) registers
+  *               ...                are cached in x0 to x7.
+  *               ...                Only positions must be stored in the first
+@@ -95,7 +93,6 @@ namespace internal {
+  *              Address end,
+  *              int* capture_output_array,
+  *              int num_capture_registers,
+- *              byte* stack_area_base,
+  *              bool direct_call = false,
+  *              Isolate* isolate,
+  *              Address regexp);
+@@ -751,11 +748,10 @@ Handle<HeapObject> RegExpMacroAssemblerARM64::GetCode(Handle<String> source) {
+   // x3:  byte*    input_end
+   // x4:  int*     output array
+   // x5:  int      output array size
+-  // x6:  Address  stack_base
+-  // x7:  int      direct_call
+-
+-  //  sp[8]:  address of the current isolate
+-  //  sp[0]:  secondary link/return address used by native call
++  // x6:  int      direct_call
++  // x7:  Isolate* isolate
++  //
++  // sp[0]:  secondary link/return address used by native call
+ 
+   // Tell the system that we have a stack frame.  Because the type is MANUAL, no
+   // code is generated.
+diff --git a/src/regexp/arm64/regexp-macro-assembler-arm64.h b/src/regexp/arm64/regexp-macro-assembler-arm64.h
+index 204ee68dc868142693e9959170c71df3f72f97ce..f3869a72b631f9fb42b275d2edd8f3cfe1cfd8bb 100644
+--- a/src/regexp/arm64/regexp-macro-assembler-arm64.h
++++ b/src/regexp/arm64/regexp-macro-assembler-arm64.h
+@@ -102,16 +102,12 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM64
+   // Callee-saved registers (x19-x28).
+   static const int kNumCalleeSavedRegisters = 10;
+   static const int kCalleeSavedRegisters = kReturnAddress + kSystemPointerSize;
+-  // Stack parameter placed by caller.
+-  // It is placed above the FP, LR and the callee-saved registers.
+-  static const int kIsolate =
+-      kCalleeSavedRegisters + kNumCalleeSavedRegisters * kSystemPointerSize;
+ 
+   // Below the frame pointer.
+   // Register parameters stored by setup code.
+-  static const int kDirectCall = -kSystemPointerSize;
+-  static const int kStackHighEnd = kDirectCall - kSystemPointerSize;
+-  static const int kOutputSize = kStackHighEnd - kSystemPointerSize;
++  static const int kIsolate = -kSystemPointerSize;
++  static const int kDirectCall = kIsolate - kSystemPointerSize;
++  static const int kOutputSize = kDirectCall - kSystemPointerSize;
+   static const int kInput = kOutputSize - kSystemPointerSize;
+   // When adding local variables remember to push space for them in
+   // the frame in GetCode.
+diff --git a/src/regexp/experimental/experimental.cc b/src/regexp/experimental/experimental.cc
+index bff2d7da6645d6023fc218daf370a51a9d404495..def43dc2c167fd463947cbbbc507da664d38aead 100644
+--- a/src/regexp/experimental/experimental.cc
++++ b/src/regexp/experimental/experimental.cc
+@@ -192,8 +192,7 @@ int32_t ExperimentalRegExp::ExecRaw(Isolate* isolate,
+ int32_t ExperimentalRegExp::MatchForCallFromJs(
+     Address subject, int32_t start_position, Address input_start,
+     Address input_end, int* output_registers, int32_t output_register_count,
+-    Address backtrack_stack, RegExp::CallOrigin call_origin, Isolate* isolate,
+-    Address regexp) {
++    RegExp::CallOrigin call_origin, Isolate* isolate, Address regexp) {
+   DCHECK(FLAG_enable_experimental_regexp_engine);
+   DCHECK_NOT_NULL(isolate);
+   DCHECK_NOT_NULL(output_registers);
+diff --git a/src/regexp/experimental/experimental.h b/src/regexp/experimental/experimental.h
+index 1b44100cc88bed7825c0a30fb05e8477c47860ec..671792e5ef82919af652f431c5f6b325fea08d77 100644
+--- a/src/regexp/experimental/experimental.h
++++ b/src/regexp/experimental/experimental.h
+@@ -33,7 +33,6 @@ class ExperimentalRegExp final : public AllStatic {
+                                     Address input_start, Address input_end,
+                                     int* output_registers,
+                                     int32_t output_register_count,
+-                                    Address backtrack_stack,
+                                     RegExp::CallOrigin call_origin,
+                                     Isolate* isolate, Address regexp);
+   static MaybeHandle<Object> Exec(
+diff --git a/src/regexp/ia32/regexp-macro-assembler-ia32.cc b/src/regexp/ia32/regexp-macro-assembler-ia32.cc
+index 51d63b2531e2bc85fb115de23d7b6a6f40b36f11..8369b88e22c300890fe4ddb1bbba62093e8b23d8 100644
+--- a/src/regexp/ia32/regexp-macro-assembler-ia32.cc
++++ b/src/regexp/ia32/regexp-macro-assembler-ia32.cc
+@@ -40,8 +40,6 @@ namespace internal {
+  *       - Isolate* isolate     (address of the current isolate)
+  *       - direct_call          (if 1, direct call from JavaScript code, if 0
+  *                               call through the runtime system)
+- *       - stack_area_base      (high end of the memory area to use as
+- *                               backtracking stack)
+  *       - capture array size   (may fit multiple sets of matches)
+  *       - int* capture_array   (int[num_saved_registers_], for output).
+  *       - end of input         (address of end of string)
+@@ -74,7 +72,6 @@ namespace internal {
+  *              Address end,
+  *              int* capture_output_array,
+  *              int num_capture_registers,
+- *              byte* stack_area_base,
+  *              bool direct_call = false,
+  *              Isolate* isolate
+  *              Address regexp);
+diff --git a/src/regexp/ia32/regexp-macro-assembler-ia32.h b/src/regexp/ia32/regexp-macro-assembler-ia32.h
+index 861795da900d91111386e4f8e660f7f94ea46a33..01914a6b8b5abb96a4eec8d844e2d1aea7cbf231 100644
+--- a/src/regexp/ia32/regexp-macro-assembler-ia32.h
++++ b/src/regexp/ia32/regexp-macro-assembler-ia32.h
+@@ -105,8 +105,7 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerIA32
+   // one set of capture results.  For the case of non-global regexp, we ignore
+   // this value.
+   static const int kNumOutputRegisters = kRegisterOutput + kSystemPointerSize;
+-  static const int kStackHighEnd = kNumOutputRegisters + kSystemPointerSize;
+-  static const int kDirectCall = kStackHighEnd + kSystemPointerSize;
++  static const int kDirectCall = kNumOutputRegisters + kSystemPointerSize;
+   static const int kIsolate = kDirectCall + kSystemPointerSize;
+   // Below the frame pointer - local stack variables.
+   // When adding local variables remember to push space for them in
+diff --git a/src/regexp/regexp-interpreter.cc b/src/regexp/regexp-interpreter.cc
+index 02fc3349208b7127d967a8b25763b3951f2c9dde..c9dd403e751502750cbb8bc4b4e820eb1136c9ea 100644
+--- a/src/regexp/regexp-interpreter.cc
++++ b/src/regexp/regexp-interpreter.cc
+@@ -1110,7 +1110,7 @@ IrregexpInterpreter::Result IrregexpInterpreter::MatchInternal(
+ // builtin.
+ IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromJs(
+     Address subject, int32_t start_position, Address, Address,
+-    int* output_registers, int32_t output_register_count, Address,
++    int* output_registers, int32_t output_register_count,
+     RegExp::CallOrigin call_origin, Isolate* isolate, Address regexp) {
+   DCHECK_NOT_NULL(isolate);
+   DCHECK_NOT_NULL(output_registers);
+diff --git a/src/regexp/regexp-interpreter.h b/src/regexp/regexp-interpreter.h
+index 9b4a8c6c307266a78a35f52ef4ef0afe5b6af6fe..19f9513acc2c7d309179ca5e18ca7ef2165e74f4 100644
+--- a/src/regexp/regexp-interpreter.h
++++ b/src/regexp/regexp-interpreter.h
+@@ -34,9 +34,8 @@ class V8_EXPORT_PRIVATE IrregexpInterpreter : public AllStatic {
+   // RETRY is returned if a retry through the runtime is needed (e.g. when
+   // interrupts have been scheduled or the regexp is marked for tier-up).
+   //
+-  // Arguments input_start, input_end and backtrack_stack are
+-  // unused. They are only passed to match the signature of the native irregex
+-  // code.
++  // Arguments input_start and input_end are unused. They are only passed to
++  // match the signature of the native irregex code.
+   //
+   // Arguments output_registers and output_register_count describe the results
+   // array, which will contain register values of all captures if SUCCESS is
+@@ -45,7 +44,6 @@ class V8_EXPORT_PRIVATE IrregexpInterpreter : public AllStatic {
+                                    Address input_start, Address input_end,
+                                    int* output_registers,
+                                    int32_t output_register_count,
+-                                   Address backtrack_stack,
+                                    RegExp::CallOrigin call_origin,
+                                    Isolate* isolate, Address regexp);
+ 
+diff --git a/src/regexp/regexp-macro-assembler.cc b/src/regexp/regexp-macro-assembler.cc
+index 27590fac36cea92f557dc0c149d9cbed6efef8b5..73313f95ec1d6e5bd7e5479e45c6b1980c638aa9 100644
+--- a/src/regexp/regexp-macro-assembler.cc
++++ b/src/regexp/regexp-macro-assembler.cc
+@@ -300,23 +300,21 @@ int NativeRegExpMacroAssembler::Execute(
+     String input,  // This needs to be the unpacked (sliced, cons) string.
+     int start_offset, const byte* input_start, const byte* input_end,
+     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()->memory_top();
+ 
+   bool is_one_byte = String::IsOneByteRepresentationUnderneath(input);
+   Code code = FromCodeT(CodeT::cast(regexp.Code(is_one_byte)));
+   RegExp::CallOrigin call_origin = RegExp::CallOrigin::kFromRuntime;
+ 
+-  using RegexpMatcherSig = int(
+-      Address input_string, int start_offset, const byte* input_start,
+-      const byte* input_end, int* output, int output_size, Address stack_base,
+-      int call_origin, Isolate* isolate, Address regexp);
++  using RegexpMatcherSig =
++      // NOLINTNEXTLINE(readability/casting)
++      int(Address input_string, int start_offset, const byte* input_start,
++          const byte* input_end, int* output, int output_size, int call_origin,
++          Isolate* isolate, Address regexp);
+ 
+   auto fn = GeneratedCode<RegexpMatcherSig>::FromCode(code);
+-  int result =
+-      fn.Call(input.ptr(), start_offset, input_start, input_end, output,
+-              output_size, stack_base, call_origin, isolate, regexp.ptr());
++  int result = fn.Call(input.ptr(), start_offset, input_start, input_end,
++                       output, output_size, call_origin, isolate, regexp.ptr());
+   DCHECK_GE(result, SMALLEST_REGEXP_RESULT);
+ 
+   if (result == EXCEPTION && !isolate->has_pending_exception()) {
+diff --git a/src/regexp/x64/regexp-macro-assembler-x64.cc b/src/regexp/x64/regexp-macro-assembler-x64.cc
+index abcbed18aaa9bdc4a497962714bffde74d581173..e4bff5dafa9f12c14805c72e51f973252b97a5a7 100644
+--- a/src/regexp/x64/regexp-macro-assembler-x64.cc
++++ b/src/regexp/x64/regexp-macro-assembler-x64.cc
+@@ -47,14 +47,12 @@ namespace internal {
+  * Each call to a C++ method should retain these registers.
+  *
+  * The stack will have the following content, in some order, indexable from the
+- * frame pointer (see, e.g., kStackHighEnd):
++ * frame pointer (see, e.g., kDirectCall):
+  *    - Address regexp       (address of the JSRegExp object; unused in native
+  *                            code, passed to match signature of interpreter)
+  *    - Isolate* isolate     (address of the current isolate)
+  *    - direct_call          (if 1, direct call from JavaScript code, if 0 call
+  *                            through the runtime system)
+- *    - stack_area_base      (high end of the memory area to use as
+- *                            backtracking stack)
+  *    - capture array size   (may fit multiple sets of matches)
+  *    - int* capture_array   (int[num_saved_registers_], for output).
+  *    - end of input         (address of end of string)
+@@ -85,7 +83,6 @@ namespace internal {
+  *              Address end,
+  *              int* capture_output_array,
+  *              int num_capture_registers,
+- *              byte* stack_area_base,
+  *              bool direct_call = false,
+  *              Isolate* isolate,
+  *              Address regexp);
+@@ -849,8 +846,6 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
+   }
+ 
+   // Initialize backtrack stack pointer.
+-  // TODO(jgruber): Remove the kStackHighEnd parameter (and others like
+-  // kIsolate).
+   LoadRegExpStackPointerFromMemory(backtrack_stackpointer());
+ 
+   __ jmp(&start_label_);
+diff --git a/src/regexp/x64/regexp-macro-assembler-x64.h b/src/regexp/x64/regexp-macro-assembler-x64.h
+index 74a3c95b06c771078ab03e6787e5912315421bb2..6f89ba9f8cf45430dc0edc7f2241a9aca34324c0 100644
+--- a/src/regexp/x64/regexp-macro-assembler-x64.h
++++ b/src/regexp/x64/regexp-macro-assembler-x64.h
+@@ -108,9 +108,8 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerX64
+   // this value. NumOutputRegisters is passed as 32-bit value.  The upper
+   // 32 bit of this 64-bit stack slot may contain garbage.
+   static const int kNumOutputRegisters = kRegisterOutput + kSystemPointerSize;
+-  static const int kStackHighEnd = kNumOutputRegisters + kSystemPointerSize;
+   // DirectCall is passed as 32 bit int (values 0 or 1).
+-  static const int kDirectCall = kStackHighEnd + kSystemPointerSize;
++  static const int kDirectCall = kNumOutputRegisters + kSystemPointerSize;
+   static const int kIsolate = kDirectCall + kSystemPointerSize;
+ #else
+   // In AMD64 ABI Calling Convention, the first six integer parameters
+@@ -121,13 +120,12 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerX64
+   static const int kInputStart = kStartIndex - kSystemPointerSize;
+   static const int kInputEnd = kInputStart - kSystemPointerSize;
+   static const int kRegisterOutput = kInputEnd - kSystemPointerSize;
+-
+   // For the case of global regular expression, we have room to store at least
+   // one set of capture results.  For the case of non-global regexp, we ignore
+   // this value.
+   static const int kNumOutputRegisters = kRegisterOutput - kSystemPointerSize;
+-  static const int kStackHighEnd = kFrameAlign;
+-  static const int kDirectCall = kStackHighEnd + kSystemPointerSize;
++
++  static const int kDirectCall = kFrameAlign;
+   static const int kIsolate = kDirectCall + kSystemPointerSize;
+ #endif
+