From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jakob Gruber <jgruber@chromium.org>
Date: Thu, 23 Sep 2021 07:26:38 +0200
Subject: Allow reentrant irregexp execution

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

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

Fixed: v8:11382
Change-Id: I5ed27630c1a64ebf3afb9ddf80fb60ea067c0c40
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3162604
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Reviewed-by: Patrick Thier <pthier@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Auto-Submit: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77013}

diff --git a/src/api/api.cc b/src/api/api.cc
index 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
 
 ##############################################################################
