Bug 1594545: Part 3: Labels and backtracking r=jandem

This patch implements labels and backtracking.

Pushing the address of a label onto the backtrack stack is somewhat tricky, because PushBacktrack is generally called before Bind. This patch adds code to store the CodeOffset that must be patched alongside the Label. (This works, because we only ever push a given label on the backtrack stack in a single place.) The actual machinery of patching will be added in a later patch.

As part of backtracking, we also implement backtrack stack limit checks. The OOL helper that is called if those checks fail will be added in a later patch.

Depends on D68414

Differential Revision: https://phabricator.services.mozilla.com/D68415

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Iain Ireland 2020-03-27 07:47:55 +00:00
Родитель 1850525f8f
Коммит 209796c6c3
4 изменённых файлов: 98 добавлений и 5 удалений

Просмотреть файл

@ -38,6 +38,11 @@ class SMRegExpMacroAssembler final : public NativeRegExpMacroAssembler {
virtual void PopCurrentPosition();
virtual void PushCurrentPosition();
virtual void Backtrack();
virtual void Bind(Label* label);
virtual void GoTo(Label* label);
virtual void PushBacktrack(Label* label);
private:
// Push a register on the backtrack stack.
@ -46,6 +51,18 @@ class SMRegExpMacroAssembler final : public NativeRegExpMacroAssembler {
// Pop a value from the backtrack stack.
void Pop(js::jit::Register target);
void JumpOrBacktrack(Label* to);
// MacroAssembler methods that take a Label can be called with a
// null label, which means that we should backtrack if we would jump
// to that label. This is a helper to avoid writing out the same
// logic a dozen times.
inline js::jit::Label* LabelOrBacktrack(Label* to) {
return to ? to->inner() : &backtrack_label_;
}
void CheckBacktrackStackLimit();
inline int char_size() { return static_cast<int>(mode_); }
inline js::jit::Scale factor() {
return mode_ == UC16 ? js::jit::TimesTwo : js::jit::TimesOne;
@ -86,8 +103,11 @@ class SMRegExpMacroAssembler final : public NativeRegExpMacroAssembler {
js::jit::Label entry_label_;
js::jit::Label start_label_;
js::jit::Label backtrack_label_;
js::jit::Label success_label_;
js::jit::Label exit_label_;
js::jit::Label stack_overflow_label_;
js::jit::Label exit_with_exception_label_;
Mode mode_;
int num_registers_;

Просмотреть файл

@ -16,10 +16,13 @@
namespace v8 {
namespace internal {
using js::jit::AbsoluteAddress;
using js::jit::Address;
using js::jit::AllocatableGeneralRegisterSet;
using js::jit::Assembler;
using js::jit::GeneralRegisterSet;
using js::jit::Imm32;
using js::jit::ImmPtr;
using js::jit::ImmWord;
using js::jit::Register;
using js::jit::StackMacroAssembler;
@ -62,13 +65,48 @@ void SMRegExpMacroAssembler::AdvanceCurrentPosition(int by) {
}
}
void SMRegExpMacroAssembler::Backtrack() {
// Check for an interrupt. We have to restart from the beginning if we
// are interrupted, so we only check for urgent interrupts.
js::jit::Label noInterrupt;
masm_.branchTest32(
Assembler::Zero, AbsoluteAddress(cx_->addressOfInterruptBits()),
Imm32(uint32_t(js::InterruptReason::CallbackUrgent)), &noInterrupt);
masm_.movePtr(ImmWord(js::RegExpRunStatus_Error), temp0_);
masm_.jump(&exit_label_);
masm_.bind(&noInterrupt);
// Pop code location from backtrack stack and jump to location.
Pop(temp0_);
masm_.jump(temp0_);
}
void SMRegExpMacroAssembler::Bind(Label* label) {
masm_.bind(label->inner());
}
void SMRegExpMacroAssembler::Fail() {
masm_.movePtr(ImmWord(js::RegExpRunStatus_Success_NotFound), temp0_);
masm_.jump(&exit_label_);
}
void SMRegExpMacroAssembler::GoTo(Label* to) {
masm_.jump(LabelOrBacktrack(to));
}
void SMRegExpMacroAssembler::PopCurrentPosition() { Pop(current_position_); }
void SMRegExpMacroAssembler::PushBacktrack(Label* label) {
MOZ_ASSERT(!label->is_bound());
MOZ_ASSERT(!label->patchOffset_.bound());
label->patchOffset_ = masm_.movWithPatch(ImmPtr(nullptr), temp0_);
MOZ_ASSERT(label->patchOffset_.bound());
Push(temp0_);
CheckBacktrackStackLimit();
}
void SMRegExpMacroAssembler::PushCurrentPosition() { Push(current_position_); }
// Returns true if a regexp match can be restarted (aka the regexp is global).
@ -92,6 +130,32 @@ void SMRegExpMacroAssembler::Pop(Register target) {
masm_.addPtr(Imm32(sizeof(void*)), backtrack_stack_pointer_);
}
void SMRegExpMacroAssembler::JumpOrBacktrack(Label* to) {
if (to) {
masm_.jump(to->inner());
} else {
Backtrack();
}
}
// Generate a quick inline test for backtrack stack overflow.
// If the test fails, call an OOL handler to try growing the stack.
void SMRegExpMacroAssembler::CheckBacktrackStackLimit() {
js::jit::Label no_stack_overflow;
masm_.branchPtr(
Assembler::BelowOrEqual,
AbsoluteAddress(isolate()->regexp_stack()->limit_address_address()),
backtrack_stack_pointer_, &no_stack_overflow);
masm_.call(&stack_overflow_label_);
// Exit with an exception if the call failed
masm_.branchTest32(Assembler::Zero, temp0_, temp0_,
&exit_with_exception_label_);
masm_.bind(&no_stack_overflow);
}
// This is only used by tracing code.
// The return value doesn't matter.
RegExpMacroAssembler::IrregexpImplementation

Просмотреть файл

@ -11,6 +11,7 @@
#include <iostream>
#include "new-regexp/regexp-shim.h"
#include "new-regexp/regexp-stack.h"
namespace v8 {
namespace internal {
@ -150,6 +151,10 @@ std::unique_ptr<char[]> String::ToCString() {
return std::unique_ptr<char[]>();
}
byte* Isolate::top_of_regexp_stack() const {
return reinterpret_cast<byte*>(regexpStack_->memory_top_address_address());
}
Handle<ByteArray> Isolate::NewByteArray(int length, AllocationType alloc) {
MOZ_RELEASE_ASSERT(length >= 0);

Просмотреть файл

@ -22,6 +22,7 @@
#include <cctype>
#include "jit/Label.h"
#include "jit/shared/Assembler-shared.h"
#include "js/Value.h"
#include "new-regexp/util/flags.h"
#include "new-regexp/util/vector.h"
@ -974,9 +975,9 @@ using Factory = Isolate;
class Isolate {
public:
//********** Isolate code **********//
RegExpStack* regexp_stack() const { return regexp_stack_; }
bool has_pending_exception() { return cx()->isExceptionPending(); }
void StackOverflow() { js::ReportOverRecursed(cx()); }
RegExpStack* regexp_stack() const { return regexpStack_; }
byte* top_of_regexp_stack() const;
void StackOverflow() {}
#ifndef V8_INTL_SUPPORT
unibrow::Mapping<unibrow::Ecma262UnCanonicalize>* jsregexp_uncanonicalize() {
@ -1055,7 +1056,7 @@ private:
friend class HandleScope;
JSContext* cx_;
RegExpStack* regexp_stack_;
RegExpStack* regexpStack_;
Counters counters_;
};
@ -1113,7 +1114,7 @@ class Label {
public:
Label() : inner_(js::jit::Label()) {}
operator js::jit::Label*() { return &inner_; }
js::jit::Label* inner() { return &inner_; }
void Unuse() { inner_.reset(); }
@ -1127,6 +1128,9 @@ class Label {
private:
js::jit::Label inner_;
js::jit::CodeOffset patchOffset_;
friend class SMRegExpMacroAssembler;
};
// TODO: Map flags to jitoptions