Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
deps: V8: cherry-pick 0dd2318b5237
Original commit message:

    [regexp] Remove DeferredAction class.

    We can just chain up the traces and use the regular
    ActionNodes that we already have to represent the
    deferred actions.  Also simplifies the ActionNodes
    a little.  No functional change intended.

    Reduce the size of the Trace from 128->120 bytes.

    This is a stack allocated struct so to avoid stack
    overflows after adding the next_ field I am reducing
    it back down to 120 bytes by rearranging and shrinking
    fields.

    Change-Id: I6dca9946e035e9b22798e160b8fadaeca61f4955
    Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6512931
    Reviewed-by: Patrick Thier <pthier@chromium.org>
    Commit-Queue: Erik Corry <erikcorry@chromium.org>
    Cr-Commit-Position: refs/heads/main@{#100092}

Refs: v8/v8@0dd2318
  • Loading branch information
Erik Corry authored and targos committed Dec 6, 2025
commit 974300dd40a7f9ffc1c631719224e5698f7d8635
2 changes: 1 addition & 1 deletion common.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

# Reset this number to 0 on major V8 upgrades.
# Increment by one for each non-official patch applied to deps/v8.
'v8_embedder_string': '-node.34',
'v8_embedder_string': '-node.35',

##### V8 defaults for Node.js #####

Expand Down
6 changes: 3 additions & 3 deletions deps/v8/src/regexp/regexp-compiler-tonode.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1248,9 +1248,9 @@ RegExpNode* RegExpCapture::ToNode(RegExpTree* body, int index,
int start_reg = RegExpCapture::StartRegister(index);
int end_reg = RegExpCapture::EndRegister(index);
if (compiler->read_backward()) std::swap(start_reg, end_reg);
RegExpNode* store_end = ActionNode::StorePosition(end_reg, true, on_success);
RegExpNode* store_end = ActionNode::ClearPosition(end_reg, on_success);
RegExpNode* body_node = body->ToNode(compiler, store_end);
return ActionNode::StorePosition(start_reg, true, body_node);
return ActionNode::ClearPosition(start_reg, body_node);
}

namespace {
Expand Down Expand Up @@ -2039,7 +2039,7 @@ RegExpNode* RegExpQuantifier::ToNode(int min, int max, bool is_greedy,
if (body_can_be_empty) {
// If the body can be empty we need to store the start position
// so we can bail out if it was empty.
body_node = ActionNode::StorePosition(body_start_reg, false, body_node);
body_node = ActionNode::RestorePosition(body_start_reg, body_node);
}
if (needs_capture_clearing) {
// Before entering the body of this loop we need to clear captures.
Expand Down
150 changes: 57 additions & 93 deletions deps/v8/src/regexp/regexp-compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -294,30 +294,20 @@ RegExpCompiler::CompilationResult RegExpCompiler::Assemble(
return {code, next_register_};
}

bool Trace::DeferredAction::Mentions(int that) {
if (action_type() == ActionNode::CLEAR_CAPTURES) {
Interval range = static_cast<DeferredClearCaptures*>(this)->range();
return range.Contains(that);
} else {
return reg() == that;
}
}

bool Trace::mentions_reg(int reg) {
for (DeferredAction* action = actions_; action != nullptr;
action = action->next()) {
if (action->Mentions(reg)) return true;
bool Trace::mentions_reg(int reg) const {
for (auto trace : *this) {
if (trace->has_action() && trace->action()->Mentions(reg)) return true;
}
return false;
}

bool Trace::GetStoredPosition(int reg, int* cp_offset) {
bool Trace::GetStoredPosition(int reg, int* cp_offset) const {
DCHECK_EQ(0, *cp_offset);
for (DeferredAction* action = actions_; action != nullptr;
action = action->next()) {
if (action->Mentions(reg)) {
if (action->action_type() == ActionNode::STORE_POSITION) {
*cp_offset = static_cast<DeferredCapture*>(action)->cp_offset();
for (auto trace : *this) {
if (trace->has_action() && trace->action()->Mentions(reg)) {
if (trace->action_->action_type() == ActionNode::CLEAR_POSITION ||
trace->action_->action_type() == ActionNode::RESTORE_POSITION) {
*cp_offset = trace->next_->cp_offset();
return true;
} else {
return false;
Expand Down Expand Up @@ -363,16 +353,13 @@ class DynamicBitSet : public ZoneObject {
int Trace::FindAffectedRegisters(DynamicBitSet* affected_registers,
Zone* zone) {
int max_register = RegExpCompiler::kNoRegister;
for (DeferredAction* action = actions_; action != nullptr;
action = action->next()) {
if (action->action_type() == ActionNode::CLEAR_CAPTURES) {
Interval range = static_cast<DeferredClearCaptures*>(action)->range();
for (int i = range.from(); i <= range.to(); i++)
for (auto trace : *this) {
if (ActionNode* action = trace->action_) {
int to = action->register_to();
for (int i = action->register_from(); i <= to; i++) {
affected_registers->Set(i, zone);
if (range.to() > max_register) max_register = range.to();
} else {
affected_registers->Set(action->reg(), zone);
if (action->reg() > max_register) max_register = action->reg();
}
if (to > max_register) max_register = to;
}
}
return max_register;
Expand Down Expand Up @@ -420,15 +407,14 @@ void Trace::PerformDeferredActions(RegExpMacroAssembler* assembler,
int store_position = kNoStore;
// This is a little tricky because we are scanning the actions in reverse
// historical order (newest first).
for (DeferredAction* action = actions_; action != nullptr;
action = action->next()) {
for (auto trace : *this) {
ActionNode* action = trace->action_;
if (!action) continue;
if (action->Mentions(reg)) {
switch (action->action_type()) {
case ActionNode::SET_REGISTER_FOR_LOOP: {
Trace::DeferredSetRegisterForLoop* psr =
static_cast<Trace::DeferredSetRegisterForLoop*>(action);
if (!absolute) {
value += psr->value();
value += action->value();
absolute = true;
}
// SET_REGISTER_FOR_LOOP is only used for newly introduced loop
Expand All @@ -449,11 +435,10 @@ void Trace::PerformDeferredActions(RegExpMacroAssembler* assembler,
DCHECK(!clear);
undo_action = RESTORE;
break;
case ActionNode::STORE_POSITION: {
Trace::DeferredCapture* pc =
static_cast<Trace::DeferredCapture*>(action);
case ActionNode::CLEAR_POSITION:
case ActionNode::RESTORE_POSITION: {
if (!clear && store_position == kNoStore) {
store_position = pc->cp_offset();
store_position = trace->next()->cp_offset();
}

// For captures we know that stores and clears alternate.
Expand All @@ -466,7 +451,11 @@ void Trace::PerformDeferredActions(RegExpMacroAssembler* assembler,
// will set it again or fail.
undo_action = IGNORE;
} else {
undo_action = pc->is_capture() ? CLEAR : RESTORE;
if (action->action_type() == ActionNode::CLEAR_POSITION) {
undo_action = CLEAR;
} else {
undo_action = RESTORE;
}
}
DCHECK(!absolute);
DCHECK_EQ(value, 0);
Expand Down Expand Up @@ -527,7 +516,7 @@ void Trace::Flush(RegExpCompiler* compiler, RegExpNode* successor) {

DCHECK(!is_trivial());

if (actions_ == nullptr && backtrack() == nullptr) {
if (!has_any_actions() && backtrack() == nullptr) {
// Here we just have some deferred cp advances to fix and we are back to
// a normal situation. We may also have to forget some information gained
// through a quick check that was already performed.
Expand Down Expand Up @@ -638,35 +627,26 @@ void GuardedAlternative::AddGuard(Guard* guard, Zone* zone) {

ActionNode* ActionNode::SetRegisterForLoop(int reg, int val,
RegExpNode* on_success) {
ActionNode* result =
on_success->zone()->New<ActionNode>(SET_REGISTER_FOR_LOOP, on_success);
result->data_.u_store_register.reg = reg;
result->data_.u_store_register.value = val;
return result;
return on_success->zone()->New<ActionNode>(SET_REGISTER_FOR_LOOP, on_success,
reg, reg, val);
}

ActionNode* ActionNode::IncrementRegister(int reg, RegExpNode* on_success) {
ActionNode* result =
on_success->zone()->New<ActionNode>(INCREMENT_REGISTER, on_success);
result->data_.u_increment_register.reg = reg;
return result;
return on_success->zone()->New<ActionNode>(INCREMENT_REGISTER, on_success,
reg);
}

ActionNode* ActionNode::StorePosition(int reg, bool is_capture,
RegExpNode* on_success) {
ActionNode* result =
on_success->zone()->New<ActionNode>(STORE_POSITION, on_success);
result->data_.u_position_register.reg = reg;
result->data_.u_position_register.is_capture = is_capture;
return result;
ActionNode* ActionNode::ClearPosition(int reg, RegExpNode* on_success) {
return on_success->zone()->New<ActionNode>(CLEAR_POSITION, on_success, reg);
}

ActionNode* ActionNode::RestorePosition(int reg, RegExpNode* on_success) {
return on_success->zone()->New<ActionNode>(RESTORE_POSITION, on_success, reg);
}

ActionNode* ActionNode::ClearCaptures(Interval range, RegExpNode* on_success) {
ActionNode* result =
on_success->zone()->New<ActionNode>(CLEAR_CAPTURES, on_success);
result->data_.u_clear_captures.range_from = range.from();
result->data_.u_clear_captures.range_to = range.to();
return result;
return on_success->zone()->New<ActionNode>(CLEAR_CAPTURES, on_success,
range.from(), range.to());
}

ActionNode* ActionNode::BeginPositiveSubmatch(int stack_reg, int position_reg,
Expand Down Expand Up @@ -2373,7 +2353,7 @@ void AssertionNode::Emit(RegExpCompiler* compiler, Trace* trace) {

namespace {

bool DeterminedAlready(QuickCheckDetails* quick_check, int offset) {
bool DeterminedAlready(const QuickCheckDetails* quick_check, int offset) {
if (quick_check == nullptr) return false;
if (offset >= quick_check->characters()) return false;
return quick_check->positions(offset)->determines_perfectly;
Expand Down Expand Up @@ -2423,7 +2403,7 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
Isolate* isolate = assembler->isolate();
bool one_byte = compiler->one_byte();
Label* backtrack = trace->backtrack();
QuickCheckDetails* quick_check = trace->quick_check_performed();
const QuickCheckDetails* quick_check = trace->quick_check_performed();
int element_count = elements()->length();
int backward_offset = read_backward() ? -Length() : 0;
for (int i = preloaded ? 0 : element_count - 1; i >= 0; i--) {
Expand Down Expand Up @@ -3206,7 +3186,7 @@ void ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) {

// For loop nodes we already flushed (see LoopChoiceNode::Emit), but for
// other choice nodes we only flush if we are out of code size budget.
if (trace->flush_budget() == 0 && trace->actions() != nullptr) {
if (trace->flush_budget() == 0 && trace->has_any_actions()) {
trace->Flush(compiler, this);
return;
}
Expand Down Expand Up @@ -3239,7 +3219,7 @@ void ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
// If there are actions to be flushed we have to limit how many times
// they are flushed. Take the budget of the parent trace and distribute
// it fairly amongst the children.
if (new_trace.actions() != nullptr) {
if (new_trace.has_any_actions()) {
new_trace.set_flush_budget(new_flush_budget);
}
bool next_expects_preload =
Expand Down Expand Up @@ -3407,7 +3387,7 @@ void ChoiceNode::EmitChoices(RegExpCompiler* compiler,
generate_full_check_inline = true;
}
if (generate_full_check_inline) {
if (new_trace.actions() != nullptr) {
if (new_trace.has_any_actions()) {
new_trace.set_flush_budget(new_flush_budget);
}
for (int j = 0; j < guard_count; j++) {
Expand Down Expand Up @@ -3468,42 +3448,26 @@ void ActionNode::Emit(RegExpCompiler* compiler, Trace* trace) {
RecursionCheck rc(compiler);

switch (action_type_) {
case STORE_POSITION: {
Trace::DeferredCapture new_capture(data_.u_position_register.reg,
data_.u_position_register.is_capture,
trace);
Trace new_trace = *trace;
new_trace.add_action(&new_capture);
on_success()->Emit(compiler, &new_trace);
break;
}
case INCREMENT_REGISTER: {
Trace::DeferredIncrementRegister new_increment(
data_.u_increment_register.reg);
Trace new_trace = *trace;
new_trace.add_action(&new_increment);
on_success()->Emit(compiler, &new_trace);
break;
}
case SET_REGISTER_FOR_LOOP: {
Trace::DeferredSetRegisterForLoop new_set(data_.u_store_register.reg,
data_.u_store_register.value);
Trace new_trace = *trace;
new_trace.add_action(&new_set);
on_success()->Emit(compiler, &new_trace);
break;
}
// Start with the actions we know how to defer. These are just recorded in
// the new trace, no code is emitted right now. (If we backtrack then we
// don't have to perform and undo these actions.)
case CLEAR_POSITION:
case RESTORE_POSITION:
case INCREMENT_REGISTER:
case SET_REGISTER_FOR_LOOP:
case CLEAR_CAPTURES: {
Trace::DeferredClearCaptures new_capture(Interval(
data_.u_clear_captures.range_from, data_.u_clear_captures.range_to));
Trace new_trace = *trace;
new_trace.add_action(&new_capture);
new_trace.add_action(this);
on_success()->Emit(compiler, &new_trace);
break;
}
// We don't yet have the ability to defer these.
case BEGIN_POSITIVE_SUBMATCH:
case BEGIN_NEGATIVE_SUBMATCH:
if (!trace->is_trivial()) {
// Complex situation: Flush the trace state to the assembler and
// generate a generic version of this action. This call will
// recurse back to the else clause here.
trace->Flush(compiler, this);
} else {
assembler->WriteCurrentPositionToRegister(
Expand Down
Loading