From 209796c6c39592604ac9d7ca1b74637917f11e31 Mon Sep 17 00:00:00 2001 From: Iain Ireland Date: Fri, 27 Mar 2020 07:47:55 +0000 Subject: [PATCH] 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 --- .../new-regexp/regexp-macro-assembler-arch.h | 20 ++++++ .../regexp-native-macro-assembler.cc | 64 +++++++++++++++++++ js/src/new-regexp/regexp-shim.cc | 5 ++ js/src/new-regexp/regexp-shim.h | 14 ++-- 4 files changed, 98 insertions(+), 5 deletions(-) diff --git a/js/src/new-regexp/regexp-macro-assembler-arch.h b/js/src/new-regexp/regexp-macro-assembler-arch.h index 41ec3901ca54..94c685de4bb8 100644 --- a/js/src/new-regexp/regexp-macro-assembler-arch.h +++ b/js/src/new-regexp/regexp-macro-assembler-arch.h @@ -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(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_; diff --git a/js/src/new-regexp/regexp-native-macro-assembler.cc b/js/src/new-regexp/regexp-native-macro-assembler.cc index 3b32756e43f1..f11c9de3ec26 100644 --- a/js/src/new-regexp/regexp-native-macro-assembler.cc +++ b/js/src/new-regexp/regexp-native-macro-assembler.cc @@ -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 diff --git a/js/src/new-regexp/regexp-shim.cc b/js/src/new-regexp/regexp-shim.cc index c290824d09bb..0ddc37d84790 100644 --- a/js/src/new-regexp/regexp-shim.cc +++ b/js/src/new-regexp/regexp-shim.cc @@ -11,6 +11,7 @@ #include #include "new-regexp/regexp-shim.h" +#include "new-regexp/regexp-stack.h" namespace v8 { namespace internal { @@ -150,6 +151,10 @@ std::unique_ptr String::ToCString() { return std::unique_ptr(); } +byte* Isolate::top_of_regexp_stack() const { + return reinterpret_cast(regexpStack_->memory_top_address_address()); +} + Handle Isolate::NewByteArray(int length, AllocationType alloc) { MOZ_RELEASE_ASSERT(length >= 0); diff --git a/js/src/new-regexp/regexp-shim.h b/js/src/new-regexp/regexp-shim.h index 002ef52f6b21..6953d40e5010 100644 --- a/js/src/new-regexp/regexp-shim.h +++ b/js/src/new-regexp/regexp-shim.h @@ -22,6 +22,7 @@ #include #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* 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