Revert "[regexp] Guarantee an allocated regexp stack"

This reverts commit 97ed8b277b.

Reason for revert: breaks chromium roll
https://chromium-review.googlesource.com/c/chromium/src/+/1864878

I bisected it down to this CL here:
https://chromium-review.googlesource.com/c/chromium/src/+/1865346/6
https://ci.chromium.org/p/chromium/builders/try/linux-rel/219610

Original change's description:
> [regexp] Guarantee an allocated regexp stack
> 
> The regexp stack is used during execution of jitted regexp matcher
> code.  Previously, the stack was initially not present / nullptr, and
> we had to explicitly check for this condition and bail out in builtin
> code.
> 
> This CL changes behavior to guarantee a present stack by adding a
> statically-allocated area that is used whenever no
> dynamically-allocated stack exists.
> 
> Change-Id: I52934425ae72cf0e5d13fab2b9d63d37ca76fcf3
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1852126
> Auto-Submit: Jakob Gruber <jgruber@chromium.org>
> Commit-Queue: Peter Marshall <petermarshall@chromium.org>
> Reviewed-by: Peter Marshall <petermarshall@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#64326}

TBR=jgruber@chromium.org,petermarshall@chromium.org

Change-Id: I085b7aebb513fdededda7631b06ff68e5ae5846e
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1864945
Reviewed-by: Sathya Gunasekaran  <gsathya@chromium.org>
Commit-Queue: Sathya Gunasekaran  <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64346}
This commit is contained in:
Sathya Gunasekaran 2019-10-17 12:01:46 +00:00 committed by Commit Bot
parent 581bf00eb2
commit 134f551248
5 changed files with 62 additions and 53 deletions

View File

@ -398,6 +398,8 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal(
ExternalConstant(ExternalReference::isolate_address(isolate()));
TNode<ExternalReference> regexp_stack_memory_top_address = ExternalConstant(
ExternalReference::address_of_regexp_stack_memory_top_address(isolate()));
TNode<ExternalReference> regexp_stack_memory_size_address = ExternalConstant(
ExternalReference::address_of_regexp_stack_memory_size(isolate()));
TNode<ExternalReference> static_offsets_vector_address = ExternalConstant(
ExternalReference::address_of_static_offsets_vector(isolate()));
@ -516,6 +518,21 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal(
GotoIf(TaggedIsSmi(var_code.value()), &runtime);
TNode<Code> code = CAST(var_code.value());
// Ensure that a RegExp stack is allocated when using compiled Irregexp.
// TODO(jgruber): Guarantee an allocated stack and remove this check.
{
Label next(this);
GotoIfNot(TaggedIsSmi(var_bytecode.value()), &next);
CSA_ASSERT(this, SmiEqual(CAST(var_bytecode.value()),
SmiConstant(JSRegExp::kUninitializedValue)));
TNode<IntPtrT> stack_size = UncheckedCast<IntPtrT>(
Load(MachineType::IntPtr(), regexp_stack_memory_size_address));
Branch(IntPtrEqual(stack_size, IntPtrZero()), &runtime, &next);
BIND(&next);
}
Label if_success(this), if_exception(this, Label::kDeferred);
{
IncrementCounter(isolate()->counters()->regexp_entry_native(), 1);

View File

@ -504,6 +504,16 @@ ExternalReference ExternalReference::address_of_regexp_stack_limit_address(
return ExternalReference(isolate->regexp_stack()->limit_address_address());
}
ExternalReference ExternalReference::address_of_regexp_stack_memory_address(
Isolate* isolate) {
return ExternalReference(isolate->regexp_stack()->memory_address_address());
}
ExternalReference ExternalReference::address_of_regexp_stack_memory_size(
Isolate* isolate) {
return ExternalReference(isolate->regexp_stack()->memory_size_address());
}
ExternalReference ExternalReference::address_of_regexp_stack_memory_top_address(
Isolate* isolate) {
return ExternalReference(

View File

@ -74,6 +74,9 @@ class StatsCounter;
V(stack_is_iterable_address, "IsolateData::stack_is_iterable_address") \
V(address_of_regexp_stack_limit_address, \
"RegExpStack::limit_address_address()") \
V(address_of_regexp_stack_memory_address, \
"RegExpStack::memory_address_address()") \
V(address_of_regexp_stack_memory_size, "RegExpStack::memory_size_address()") \
V(address_of_regexp_stack_memory_top_address, \
"RegExpStack::memory_top_address_address()") \
V(address_of_static_offsets_vector, "OffsetsVector::static_offsets_vector") \

View File

@ -22,7 +22,7 @@ RegExpStackScope::~RegExpStackScope() {
regexp_stack_->Reset();
}
RegExpStack::RegExpStack() : thread_local_(this), isolate_(nullptr) {}
RegExpStack::RegExpStack() : isolate_(nullptr) {}
RegExpStack::~RegExpStack() {
thread_local_.Free();
@ -30,17 +30,9 @@ RegExpStack::~RegExpStack() {
char* RegExpStack::ArchiveStack(char* to) {
if (!thread_local_.owns_memory_) {
// Force dynamic stacks prior to archiving. Any growth will do. A dynamic
// stack is needed because stack archival & restoration rely on `memory_`
// pointing at a fixed-location backing store, whereas the static stack is
// tied to a RegExpStack instance.
EnsureCapacity(thread_local_.memory_size_ + 1);
}
size_t size = sizeof(thread_local_);
MemCopy(reinterpret_cast<void*>(to), &thread_local_, size);
thread_local_ = ThreadLocal(this);
thread_local_ = ThreadLocal();
return to + size;
}
@ -53,44 +45,37 @@ char* RegExpStack::RestoreStack(char* from) {
void RegExpStack::Reset() {
STATIC_ASSERT(kMinimumDynamicStackSize > kStaticStackSize);
if (thread_local_.memory_size_ > kMinimumDynamicStackSize) {
DCHECK(thread_local_.owns_memory_);
if (thread_local_.memory_size_ > kMinimumStackSize) {
DeleteArray(thread_local_.memory_);
thread_local_ = ThreadLocal(this);
thread_local_ = ThreadLocal();
}
}
void RegExpStack::ThreadLocal::Free() {
if (owns_memory_) DeleteArray(memory_);
// This stack may not be used after being freed. Just reset to invalid values
// to ensure we don't accidentally use old memory areas.
memory_ = nullptr;
memory_top_ = nullptr;
memory_size_ = 0;
limit_ = kMemoryTop;
if (memory_size_ > 0) {
DeleteArray(memory_);
Clear();
}
}
Address RegExpStack::EnsureCapacity(size_t size) {
if (size > kMaximumStackSize) return kNullAddress;
if (size < kMinimumDynamicStackSize) size = kMinimumDynamicStackSize;
if (size < kMinimumStackSize) size = kMinimumStackSize;
if (thread_local_.memory_size_ < size) {
byte* new_memory = NewArray<byte>(size);
if (thread_local_.memory_size_ > 0) {
// Copy original memory into top of new memory.
MemCopy(new_memory + size - thread_local_.memory_size_,
thread_local_.memory_, thread_local_.memory_size_);
if (thread_local_.owns_memory_) DeleteArray(thread_local_.memory_);
DeleteArray(thread_local_.memory_);
}
thread_local_.memory_ = new_memory;
thread_local_.memory_top_ = new_memory + size;
thread_local_.memory_size_ = size;
thread_local_.limit_ = reinterpret_cast<Address>(new_memory) +
kStackLimitSlack * kSystemPointerSize;
thread_local_.owns_memory_ = true;
}
return reinterpret_cast<Address>(thread_local_.memory_top_);
}

View File

@ -41,7 +41,7 @@ class RegExpStack {
// 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.
static constexpr int kStackLimitSlack = 32;
static const int kStackLimitSlack = 32;
// Gives the top of the memory used as stack.
Address stack_base() {
@ -66,7 +66,7 @@ class RegExpStack {
Address EnsureCapacity(size_t size);
// Thread local archiving.
static constexpr int ArchiveSpacePerThread() {
static int ArchiveSpacePerThread() {
return static_cast<int>(sizeof(ThreadLocal));
}
char* ArchiveStack(char* to);
@ -80,47 +80,41 @@ class RegExpStack {
RegExpStack();
~RegExpStack();
// Artificial limit used when the thread-local state has been destroyed.
// Artificial limit used when no memory has been allocated.
static const Address kMemoryTop =
static_cast<Address>(static_cast<uintptr_t>(-1));
// Minimal size of dynamically-allocated stack area.
static constexpr size_t kMinimumDynamicStackSize = 1 * KB;
// In addition to dynamically-allocated, variable-sized stacks, we also have
// a statically allocated and sized area that is used whenever no dynamic
// stack is allocated. This guarantees that a stack is always available and
// we can skip availability-checks later on.
// It's double the slack size to ensure that we have a bit of breathing room
// before NativeRegExpMacroAssembler::GrowStack must be called.
static constexpr size_t kStaticStackSize =
2 * kStackLimitSlack * kSystemPointerSize;
byte static_stack_[kStaticStackSize] = {0};
STATIC_ASSERT(kStaticStackSize <= kMaximumStackSize);
// Minimal size of allocated stack area.
static const size_t kMinimumStackSize = 1 * KB;
// Structure holding the allocated memory, size and limit.
struct ThreadLocal {
explicit ThreadLocal(RegExpStack* regexp_stack) {
memory_ = regexp_stack->static_stack_;
memory_top_ = regexp_stack->static_stack_ + kStaticStackSize;
memory_size_ = kStaticStackSize;
limit_ = reinterpret_cast<Address>(regexp_stack->static_stack_) +
kStackLimitSlack * kSystemPointerSize;
owns_memory_ = false;
}
ThreadLocal() { Clear(); }
// If memory_size_ > 0 then memory_ and memory_top_ must be non-nullptr
// and memory_top_ = memory_ + memory_size_
byte* memory_;
byte* memory_top_;
size_t memory_size_;
Address limit_;
bool owns_memory_; // Whether memory_ is owned and must be freed.
void Clear() {
memory_ = nullptr;
memory_top_ = nullptr;
memory_size_ = 0;
limit_ = kMemoryTop;
}
void Free();
};
// Address of allocated memory.
Address memory_address_address() {
return reinterpret_cast<Address>(&thread_local_.memory_);
}
// Address of size of allocated memory.
Address memory_size_address() {
return reinterpret_cast<Address>(&thread_local_.memory_size_);
}
// Address of top of memory used as stack.
Address memory_top_address_address() {
return reinterpret_cast<Address>(&thread_local_.memory_top_);