| Index: src/jsregexp.cc
|
| ===================================================================
|
| --- src/jsregexp.cc (revision 1074)
|
| +++ src/jsregexp.cc (working copy)
|
| @@ -1110,11 +1110,10 @@
|
| // Execution state virtualization.
|
| //
|
| // Instead of emitting code, nodes that manipulate the state can record their
|
| -// manipulation in an object called the GenerationVariant. The
|
| -// GenerationVariant object can record a current position offset, an
|
| -// optional backtrack code location on the top of the virtualized backtrack
|
| -// stack and some register changes. When a node is to be emitted it can flush
|
| -// the GenerationVariant or update it. Flushing the GenerationVariant
|
| +// manipulation in an object called the Trace. The Trace object can record a
|
| +// current position offset, an optional backtrack code location on the top of
|
| +// the virtualized backtrack stack and some register changes. When a node is
|
| +// to be emitted it can flush the Trace or update it. Flushing the Trace
|
| // will emit code to bring the actual state into line with the virtual state.
|
| // Avoiding flushing the state can postpone some work (eg updates of capture
|
| // registers). Postponing work can save time when executing the regular
|
| @@ -1123,20 +1122,19 @@
|
| // known backtrack code location than it is to pop an unknown backtrack
|
| // location from the stack and jump there.
|
| //
|
| -// The virtual state found in the GenerationVariant affects code generation.
|
| -// For example the virtual state contains the difference between the actual
|
| -// current position and the virtual current position, and matching code needs
|
| -// to use this offset to attempt a match in the correct location of the input
|
| -// string. Therefore code generated for a non-trivial GenerationVariant is
|
| -// specialized to that GenerationVariant. The code generator therefore
|
| -// has the ability to generate code for each node several times. In order to
|
| -// limit the size of the generated code there is an arbitrary limit on how
|
| -// many specialized sets of code may be generated for a given node. If the
|
| -// limit is reached, the GenerationVariant is flushed and a generic version of
|
| -// the code for a node is emitted. This is subsequently used for that node.
|
| -// The code emitted for non-generic GenerationVariants is not recorded in the
|
| -// node and so it cannot currently be reused in the event that code generation
|
| -// is requested for an identical GenerationVariant.
|
| +// The virtual state found in the Trace affects code generation. For example
|
| +// the virtual state contains the difference between the actual current
|
| +// position and the virtual current position, and matching code needs to use
|
| +// this offset to attempt a match in the correct location of the input
|
| +// string. Therefore code generated for a non-trivial trace is specialized
|
| +// to that trace. The code generator therefore has the ability to generate
|
| +// code for each node several times. In order to limit the size of the
|
| +// generated code there is an arbitrary limit on how many specialized sets of
|
| +// code may be generated for a given node. If the limit is reached, the
|
| +// trace is flushed and a generic version of the code for a node is emitted.
|
| +// This is subsequently used for that node. The code emitted for non-generic
|
| +// trace is not recorded in the node and so it cannot currently be reused in
|
| +// the event that code generation is requested for an identical trace.
|
|
|
|
|
| void RegExpTree::AppendToText(RegExpText* text) {
|
| @@ -1273,15 +1271,15 @@
|
| work_list_ = &work_list;
|
| Label fail;
|
| macro_assembler->PushBacktrack(&fail);
|
| - GenerationVariant generic_variant;
|
| - if (!start->Emit(this, &generic_variant)) {
|
| + Trace new_trace;
|
| + if (!start->Emit(this, &new_trace)) {
|
| fail.Unuse();
|
| return Handle<FixedArray>::null();
|
| }
|
| macro_assembler_->Bind(&fail);
|
| macro_assembler_->Fail();
|
| while (!work_list.is_empty()) {
|
| - if (!work_list.RemoveLast()->Emit(this, &generic_variant)) {
|
| + if (!work_list.RemoveLast()->Emit(this, &new_trace)) {
|
| return Handle<FixedArray>::null();
|
| }
|
| }
|
| @@ -1304,7 +1302,7 @@
|
| return array;
|
| }
|
|
|
| -bool GenerationVariant::DeferredAction::Mentions(int that) {
|
| +bool Trace::DeferredAction::Mentions(int that) {
|
| if (type() == ActionNode::CLEAR_CAPTURES) {
|
| Interval range = static_cast<DeferredClearCaptures*>(this)->range();
|
| return range.Contains(that);
|
| @@ -1314,7 +1312,7 @@
|
| }
|
|
|
|
|
| -bool GenerationVariant::mentions_reg(int reg) {
|
| +bool Trace::mentions_reg(int reg) {
|
| for (DeferredAction* action = actions_;
|
| action != NULL;
|
| action = action->next()) {
|
| @@ -1325,7 +1323,7 @@
|
| }
|
|
|
|
|
| -bool GenerationVariant::GetStoredPosition(int reg, int* cp_offset) {
|
| +bool Trace::GetStoredPosition(int reg, int* cp_offset) {
|
| ASSERT_EQ(0, *cp_offset);
|
| for (DeferredAction* action = actions_;
|
| action != NULL;
|
| @@ -1343,7 +1341,7 @@
|
| }
|
|
|
|
|
| -int GenerationVariant::FindAffectedRegisters(OutSet* affected_registers) {
|
| +int Trace::FindAffectedRegisters(OutSet* affected_registers) {
|
| int max_register = RegExpCompiler::kNoRegister;
|
| for (DeferredAction* action = actions_;
|
| action != NULL;
|
| @@ -1362,9 +1360,9 @@
|
| }
|
|
|
|
|
| -void GenerationVariant::PushAffectedRegisters(RegExpMacroAssembler* assembler,
|
| - int max_register,
|
| - OutSet& affected_registers) {
|
| +void Trace::PushAffectedRegisters(RegExpMacroAssembler* assembler,
|
| + int max_register,
|
| + OutSet& affected_registers) {
|
| // Stay safe and check every half times the limit.
|
| // (Round up in case the limit is 1).
|
| int push_limit = (assembler->stack_limit_slack() + 1) / 2;
|
| @@ -1381,19 +1379,18 @@
|
| }
|
|
|
|
|
| -void GenerationVariant::RestoreAffectedRegisters(
|
| - RegExpMacroAssembler* assembler,
|
| - int max_register,
|
| - OutSet& affected_registers) {
|
| +void Trace::RestoreAffectedRegisters(RegExpMacroAssembler* assembler,
|
| + int max_register,
|
| + OutSet& affected_registers) {
|
| for (int reg = max_register; reg >= 0; reg--) {
|
| if (affected_registers.Get(reg)) assembler->PopRegister(reg);
|
| }
|
| }
|
|
|
|
|
| -void GenerationVariant::PerformDeferredActions(RegExpMacroAssembler* assembler,
|
| - int max_register,
|
| - OutSet& affected_registers) {
|
| +void Trace::PerformDeferredActions(RegExpMacroAssembler* assembler,
|
| + int max_register,
|
| + OutSet& affected_registers) {
|
| for (int reg = 0; reg <= max_register; reg++) {
|
| if (!affected_registers.Get(reg)) {
|
| continue;
|
| @@ -1410,8 +1407,8 @@
|
| if (action->Mentions(reg)) {
|
| switch (action->type()) {
|
| case ActionNode::SET_REGISTER: {
|
| - GenerationVariant::DeferredSetRegister* psr =
|
| - static_cast<GenerationVariant::DeferredSetRegister*>(action);
|
| + Trace::DeferredSetRegister* psr =
|
| + static_cast<Trace::DeferredSetRegister*>(action);
|
| value += psr->value();
|
| absolute = true;
|
| ASSERT_EQ(store_position, -1);
|
| @@ -1426,8 +1423,8 @@
|
| ASSERT(!clear);
|
| break;
|
| case ActionNode::STORE_POSITION: {
|
| - GenerationVariant::DeferredCapture* pc =
|
| - static_cast<GenerationVariant::DeferredCapture*>(action);
|
| + Trace::DeferredCapture* pc =
|
| + static_cast<Trace::DeferredCapture*>(action);
|
| if (!clear && store_position == -1) {
|
| store_position = pc->cp_offset();
|
| }
|
| @@ -1467,7 +1464,7 @@
|
| // This is called as we come into a loop choice node and some other tricky
|
| // nodes. It normalises the state of the code generator to ensure we can
|
| // generate generic code.
|
| -bool GenerationVariant::Flush(RegExpCompiler* compiler, RegExpNode* successor) {
|
| +bool Trace::Flush(RegExpCompiler* compiler, RegExpNode* successor) {
|
| RegExpMacroAssembler* assembler = compiler->macro_assembler();
|
|
|
| ASSERT(actions_ != NULL ||
|
| @@ -1483,7 +1480,7 @@
|
| // through a quick check that was already performed.
|
| if (cp_offset_ != 0) assembler->AdvanceCurrentPosition(cp_offset_);
|
| // Create a new trivial state and generate the node with that.
|
| - GenerationVariant new_state;
|
| + Trace new_state;
|
| return successor->Emit(compiler, &new_state);
|
| }
|
|
|
| @@ -1505,7 +1502,7 @@
|
| // Create a new trivial state and generate the node with that.
|
| Label undo;
|
| assembler->PushBacktrack(&undo);
|
| - GenerationVariant new_state;
|
| + Trace new_state;
|
| bool ok = successor->Emit(compiler, &new_state);
|
|
|
| // On backtrack we need to restore state.
|
| @@ -1525,29 +1522,27 @@
|
| }
|
|
|
|
|
| -void EndNode::EmitInfoChecks(RegExpMacroAssembler* assembler,
|
| - GenerationVariant* variant) {
|
| +void EndNode::EmitInfoChecks(RegExpMacroAssembler* assembler, Trace* trace) {
|
| if (info()->at_end) {
|
| Label succeed;
|
| // LoadCurrentCharacter will go to the label if we are at the end of the
|
| // input string.
|
| assembler->LoadCurrentCharacter(0, &succeed);
|
| - assembler->GoTo(variant->backtrack());
|
| + assembler->GoTo(trace->backtrack());
|
| assembler->Bind(&succeed);
|
| }
|
| }
|
|
|
|
|
| -bool NegativeSubmatchSuccess::Emit(RegExpCompiler* compiler,
|
| - GenerationVariant* variant) {
|
| - if (!variant->is_trivial()) {
|
| - return variant->Flush(compiler, this);
|
| +bool NegativeSubmatchSuccess::Emit(RegExpCompiler* compiler, Trace* trace) {
|
| + if (!trace->is_trivial()) {
|
| + return trace->Flush(compiler, this);
|
| }
|
| RegExpMacroAssembler* assembler = compiler->macro_assembler();
|
| if (!label()->is_bound()) {
|
| assembler->Bind(label());
|
| }
|
| - EmitInfoChecks(assembler, variant);
|
| + EmitInfoChecks(assembler, trace);
|
| assembler->ReadCurrentPositionFromRegister(current_position_register_);
|
| assembler->ReadStackPointerFromRegister(stack_pointer_register_);
|
| // Now that we have unwound the stack we find at the top of the stack the
|
| @@ -1557,9 +1552,9 @@
|
| }
|
|
|
|
|
| -bool EndNode::Emit(RegExpCompiler* compiler, GenerationVariant* variant) {
|
| - if (!variant->is_trivial()) {
|
| - return variant->Flush(compiler, this);
|
| +bool EndNode::Emit(RegExpCompiler* compiler, Trace* trace) {
|
| + if (!trace->is_trivial()) {
|
| + return trace->Flush(compiler, this);
|
| }
|
| RegExpMacroAssembler* assembler = compiler->macro_assembler();
|
| if (!label()->is_bound()) {
|
| @@ -1567,12 +1562,12 @@
|
| }
|
| switch (action_) {
|
| case ACCEPT:
|
| - EmitInfoChecks(assembler, variant);
|
| + EmitInfoChecks(assembler, trace);
|
| assembler->Succeed();
|
| return true;
|
| case BACKTRACK:
|
| ASSERT(!info()->at_end);
|
| - assembler->GoTo(variant->backtrack());
|
| + assembler->GoTo(trace->backtrack());
|
| return true;
|
| case NEGATIVE_SUBMATCH_SUCCESS:
|
| // This case is handled in a different virtual method.
|
| @@ -1674,19 +1669,19 @@
|
|
|
| void ChoiceNode::GenerateGuard(RegExpMacroAssembler* macro_assembler,
|
| Guard* guard,
|
| - GenerationVariant* variant) {
|
| + Trace* trace) {
|
| switch (guard->op()) {
|
| case Guard::LT:
|
| - ASSERT(!variant->mentions_reg(guard->reg()));
|
| + ASSERT(!trace->mentions_reg(guard->reg()));
|
| macro_assembler->IfRegisterGE(guard->reg(),
|
| guard->value(),
|
| - variant->backtrack());
|
| + trace->backtrack());
|
| break;
|
| case Guard::GEQ:
|
| - ASSERT(!variant->mentions_reg(guard->reg()));
|
| + ASSERT(!trace->mentions_reg(guard->reg()));
|
| macro_assembler->IfRegisterLT(guard->reg(),
|
| guard->value(),
|
| - variant->backtrack());
|
| + trace->backtrack());
|
| break;
|
| }
|
| }
|
| @@ -1939,7 +1934,7 @@
|
|
|
|
|
| RegExpNode::LimitResult RegExpNode::LimitVersions(RegExpCompiler* compiler,
|
| - GenerationVariant* variant) {
|
| + Trace* trace) {
|
| // TODO(erikcorry): Implement support.
|
| if (info_.follows_word_interest ||
|
| info_.follows_newline_interest ||
|
| @@ -1948,12 +1943,12 @@
|
| }
|
|
|
| // If we are generating a greedy loop then don't stop and don't reuse code.
|
| - if (variant->stop_node() != NULL) {
|
| + if (trace->stop_node() != NULL) {
|
| return CONTINUE;
|
| }
|
|
|
| RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
|
| - if (variant->is_trivial()) {
|
| + if (trace->is_trivial()) {
|
| if (label_.is_bound()) {
|
| // We are being asked to generate a generic version, but that's already
|
| // been done so just go to it.
|
| @@ -1974,16 +1969,16 @@
|
|
|
| // We are being asked to make a non-generic version. Keep track of how many
|
| // non-generic versions we generate so as not to overdo it.
|
| - variants_generated_++;
|
| - if (variants_generated_ < kMaxVariantsGenerated &&
|
| + trace_count_++;
|
| + if (trace_count_ < kMaxCopiesCodeGenerated &&
|
| compiler->recursion_depth() <= RegExpCompiler::kMaxRecursion) {
|
| return CONTINUE;
|
| }
|
|
|
| - // If we get here there have been too many variants generated or recursion
|
| - // is too deep. Time to switch to a generic version. The code for
|
| + // If we get here code has been generated for this node too many times or
|
| + // recursion is too deep. Time to switch to a generic version. The code for
|
| // generic versions above can handle deep recursion properly.
|
| - bool ok = variant->Flush(compiler, this);
|
| + bool ok = trace->Flush(compiler, this);
|
| return ok ? DONE : FAIL;
|
| }
|
|
|
| @@ -2064,7 +2059,7 @@
|
|
|
|
|
| bool RegExpNode::EmitQuickCheck(RegExpCompiler* compiler,
|
| - GenerationVariant* variant,
|
| + Trace* trace,
|
| bool preload_has_checked_bounds,
|
| Label* on_possible_success,
|
| QuickCheckDetails* details,
|
| @@ -2077,9 +2072,9 @@
|
|
|
| RegExpMacroAssembler* assembler = compiler->macro_assembler();
|
|
|
| - if (variant->characters_preloaded() != details->characters()) {
|
| - assembler->LoadCurrentCharacter(variant->cp_offset(),
|
| - variant->backtrack(),
|
| + if (trace->characters_preloaded() != details->characters()) {
|
| + assembler->LoadCurrentCharacter(trace->cp_offset(),
|
| + trace->backtrack(),
|
| !preload_has_checked_bounds,
|
| details->characters());
|
| }
|
| @@ -2116,9 +2111,9 @@
|
| }
|
| } else {
|
| if (need_mask) {
|
| - assembler->CheckNotCharacterAfterAnd(value, mask, variant->backtrack());
|
| + assembler->CheckNotCharacterAfterAnd(value, mask, trace->backtrack());
|
| } else {
|
| - assembler->CheckNotCharacter(value, variant->backtrack());
|
| + assembler->CheckNotCharacter(value, trace->backtrack());
|
| }
|
| }
|
| return true;
|
| @@ -2368,7 +2363,7 @@
|
| // first_element_checked to indicate that that character does not need to be
|
| // checked again.
|
| //
|
| -// In addition to all this we are passed a GenerationVariant, which can
|
| +// In addition to all this we are passed a Trace, which can
|
| // contain an AlternativeGeneration object. In this AlternativeGeneration
|
| // object we can see details of any quick check that was already passed in
|
| // order to get to the code we are now generating. The quick check can involve
|
| @@ -2379,17 +2374,17 @@
|
| void TextNode::TextEmitPass(RegExpCompiler* compiler,
|
| TextEmitPassType pass,
|
| bool preloaded,
|
| - GenerationVariant* variant,
|
| + Trace* trace,
|
| bool first_element_checked,
|
| int* checked_up_to) {
|
| RegExpMacroAssembler* assembler = compiler->macro_assembler();
|
| bool ascii = compiler->ascii();
|
| - Label* backtrack = variant->backtrack();
|
| - QuickCheckDetails* quick_check = variant->quick_check_performed();
|
| + Label* backtrack = trace->backtrack();
|
| + QuickCheckDetails* quick_check = trace->quick_check_performed();
|
| int element_count = elms_->length();
|
| for (int i = preloaded ? 0 : element_count - 1; i >= 0; i--) {
|
| TextElement elm = elms_->at(i);
|
| - int cp_offset = variant->cp_offset() + elm.cp_offset;
|
| + int cp_offset = trace->cp_offset() + elm.cp_offset;
|
| if (elm.type == TextElement::ATOM) {
|
| if (pass == NON_ASCII_MATCH ||
|
| pass == CHARACTER_MATCH ||
|
| @@ -2486,8 +2481,8 @@
|
| // pass from left to right. Instead we pass over the text node several times,
|
| // emitting code for some character positions every time. See the comment on
|
| // TextEmitPass for details.
|
| -bool TextNode::Emit(RegExpCompiler* compiler, GenerationVariant* variant) {
|
| - LimitResult limit_result = LimitVersions(compiler, variant);
|
| +bool TextNode::Emit(RegExpCompiler* compiler, Trace* trace) {
|
| + LimitResult limit_result = LimitVersions(compiler, trace);
|
| if (limit_result == FAIL) return false;
|
| if (limit_result == DONE) return true;
|
| ASSERT(limit_result == CONTINUE);
|
| @@ -2499,40 +2494,40 @@
|
| }
|
|
|
| if (info()->at_end) {
|
| - compiler->macro_assembler()->GoTo(variant->backtrack());
|
| + compiler->macro_assembler()->GoTo(trace->backtrack());
|
| return true;
|
| }
|
|
|
| if (compiler->ascii()) {
|
| int dummy = 0;
|
| - TextEmitPass(compiler, NON_ASCII_MATCH, false, variant, false, &dummy);
|
| + TextEmitPass(compiler, NON_ASCII_MATCH, false, trace, false, &dummy);
|
| }
|
|
|
| bool first_elt_done = false;
|
| - int bound_checked_to = variant->cp_offset() - 1;
|
| - bound_checked_to += variant->bound_checked_up_to();
|
| + int bound_checked_to = trace->cp_offset() - 1;
|
| + bound_checked_to += trace->bound_checked_up_to();
|
|
|
| // If a character is preloaded into the current character register then
|
| // check that now.
|
| - if (variant->characters_preloaded() == 1) {
|
| + if (trace->characters_preloaded() == 1) {
|
| TextEmitPass(compiler,
|
| CHARACTER_MATCH,
|
| true,
|
| - variant,
|
| + trace,
|
| false,
|
| &bound_checked_to);
|
| if (compiler->ignore_case()) {
|
| TextEmitPass(compiler,
|
| CASE_CHARACTER_MATCH,
|
| true,
|
| - variant,
|
| + trace,
|
| false,
|
| &bound_checked_to);
|
| }
|
| TextEmitPass(compiler,
|
| CHARACTER_CLASS_MATCH,
|
| true,
|
| - variant,
|
| + trace,
|
| false,
|
| &bound_checked_to);
|
| first_elt_done = true;
|
| @@ -2541,32 +2536,32 @@
|
| TextEmitPass(compiler,
|
| CHARACTER_MATCH,
|
| false,
|
| - variant,
|
| + trace,
|
| first_elt_done,
|
| &bound_checked_to);
|
| if (compiler->ignore_case()) {
|
| TextEmitPass(compiler,
|
| CASE_CHARACTER_MATCH,
|
| false,
|
| - variant,
|
| + trace,
|
| first_elt_done,
|
| &bound_checked_to);
|
| }
|
| TextEmitPass(compiler,
|
| CHARACTER_CLASS_MATCH,
|
| false,
|
| - variant,
|
| + trace,
|
| first_elt_done,
|
| &bound_checked_to);
|
|
|
| - GenerationVariant successor_variant(*variant);
|
| - successor_variant.AdvanceVariant(Length(), compiler->ascii());
|
| + Trace successor_trace(*trace);
|
| + successor_trace.AdvanceCurrentPositionInTrace(Length(), compiler->ascii());
|
| RecursionCheck rc(compiler);
|
| - return on_success()->Emit(compiler, &successor_variant);
|
| + return on_success()->Emit(compiler, &successor_trace);
|
| }
|
|
|
|
|
| -void GenerationVariant::AdvanceVariant(int by, bool ascii) {
|
| +void Trace::AdvanceCurrentPositionInTrace(int by, bool ascii) {
|
| ASSERT(by > 0);
|
| // We don't have an instruction for shifting the current character register
|
| // down or for using a shifted value for anything so lets just forget that
|
| @@ -2653,24 +2648,23 @@
|
| }
|
|
|
|
|
| -bool LoopChoiceNode::Emit(RegExpCompiler* compiler,
|
| - GenerationVariant* variant) {
|
| +bool LoopChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
|
| RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
|
| - if (variant->stop_node() == this) {
|
| + if (trace->stop_node() == this) {
|
| int text_length = GreedyLoopTextLength(&(alternatives_->at(0)));
|
| ASSERT(text_length != kNodeIsTooComplexForGreedyLoops);
|
| // Update the counter-based backtracking info on the stack. This is an
|
| // optimization for greedy loops (see below).
|
| - ASSERT(variant->cp_offset() == text_length);
|
| + ASSERT(trace->cp_offset() == text_length);
|
| macro_assembler->AdvanceCurrentPosition(text_length);
|
| - macro_assembler->GoTo(variant->loop_label());
|
| + macro_assembler->GoTo(trace->loop_label());
|
| return true;
|
| }
|
| - ASSERT(variant->stop_node() == NULL);
|
| - if (!variant->is_trivial()) {
|
| - return variant->Flush(compiler, this);
|
| + ASSERT(trace->stop_node() == NULL);
|
| + if (!trace->is_trivial()) {
|
| + return trace->Flush(compiler, this);
|
| }
|
| - return ChoiceNode::Emit(compiler, variant);
|
| + return ChoiceNode::Emit(compiler, trace);
|
| }
|
|
|
|
|
| @@ -2823,7 +2817,7 @@
|
| */
|
|
|
|
|
| -bool ChoiceNode::Emit(RegExpCompiler* compiler, GenerationVariant* variant) {
|
| +bool ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
|
| RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
|
| int choice_count = alternatives_->length();
|
| #ifdef DEBUG
|
| @@ -2832,25 +2826,25 @@
|
| ZoneList<Guard*>* guards = alternative.guards();
|
| int guard_count = (guards == NULL) ? 0 : guards->length();
|
| for (int j = 0; j < guard_count; j++) {
|
| - ASSERT(!variant->mentions_reg(guards->at(j)->reg()));
|
| + ASSERT(!trace->mentions_reg(guards->at(j)->reg()));
|
| }
|
| }
|
| #endif
|
|
|
| - LimitResult limit_result = LimitVersions(compiler, variant);
|
| + LimitResult limit_result = LimitVersions(compiler, trace);
|
| if (limit_result == DONE) return true;
|
| if (limit_result == FAIL) return false;
|
| ASSERT(limit_result == CONTINUE);
|
|
|
| RecursionCheck rc(compiler);
|
|
|
| - GenerationVariant* current_variant = variant;
|
| + Trace* current_trace = trace;
|
|
|
| int text_length = GreedyLoopTextLength(&(alternatives_->at(0)));
|
| bool greedy_loop = false;
|
| Label greedy_loop_label;
|
| - GenerationVariant counter_backtrack_variant;
|
| - counter_backtrack_variant.set_backtrack(&greedy_loop_label);
|
| + Trace counter_backtrack_trace;
|
| + counter_backtrack_trace.set_backtrack(&greedy_loop_label);
|
| if (choice_count > 1 && text_length != kNodeIsTooComplexForGreedyLoops) {
|
| // Here we have special handling for greedy loops containing only text nodes
|
| // and other simple nodes. These are handled by pushing the current
|
| @@ -2860,18 +2854,18 @@
|
| // information for each iteration of the loop, which could take up a lot of
|
| // space.
|
| greedy_loop = true;
|
| - ASSERT(variant->stop_node() == NULL);
|
| + ASSERT(trace->stop_node() == NULL);
|
| macro_assembler->PushCurrentPosition();
|
| - current_variant = &counter_backtrack_variant;
|
| + current_trace = &counter_backtrack_trace;
|
| Label greedy_match_failed;
|
| - GenerationVariant greedy_match_variant;
|
| - greedy_match_variant.set_backtrack(&greedy_match_failed);
|
| + Trace greedy_match_trace;
|
| + greedy_match_trace.set_backtrack(&greedy_match_failed);
|
| Label loop_label;
|
| macro_assembler->Bind(&loop_label);
|
| - greedy_match_variant.set_stop_node(this);
|
| - greedy_match_variant.set_loop_label(&loop_label);
|
| + greedy_match_trace.set_stop_node(this);
|
| + greedy_match_trace.set_loop_label(&loop_label);
|
| bool ok = alternatives_->at(0).node()->Emit(compiler,
|
| - &greedy_match_variant);
|
| + &greedy_match_trace);
|
| macro_assembler->Bind(&greedy_match_failed);
|
| if (!ok) {
|
| greedy_loop_label.Unuse();
|
| @@ -2886,7 +2880,7 @@
|
|
|
| int preload_characters = CalculatePreloadCharacters(compiler);
|
| bool preload_is_current =
|
| - (current_variant->characters_preloaded() == preload_characters);
|
| + (current_trace->characters_preloaded() == preload_characters);
|
| bool preload_has_checked_bounds = preload_is_current;
|
|
|
| AlternativeGenerationList alt_gens(choice_count);
|
| @@ -2899,18 +2893,18 @@
|
| alt_gen->quick_check_details.set_characters(preload_characters);
|
| ZoneList<Guard*>* guards = alternative.guards();
|
| int guard_count = (guards == NULL) ? 0 : guards->length();
|
| - GenerationVariant new_variant(*current_variant);
|
| - new_variant.set_characters_preloaded(preload_is_current ?
|
| + Trace new_trace(*current_trace);
|
| + new_trace.set_characters_preloaded(preload_is_current ?
|
| preload_characters :
|
| 0);
|
| if (preload_has_checked_bounds) {
|
| - new_variant.set_bound_checked_up_to(preload_characters);
|
| + new_trace.set_bound_checked_up_to(preload_characters);
|
| }
|
| - new_variant.quick_check_performed()->Clear();
|
| + new_trace.quick_check_performed()->Clear();
|
| alt_gen->expects_preload = preload_is_current;
|
| bool generate_full_check_inline = false;
|
| if (alternative.node()->EmitQuickCheck(compiler,
|
| - &new_variant,
|
| + &new_trace,
|
| preload_has_checked_bounds,
|
| &alt_gen->possible_success,
|
| &alt_gen->quick_check_details,
|
| @@ -2923,9 +2917,9 @@
|
| // generate the full check inline.
|
| if (i == choice_count - 1) {
|
| macro_assembler->Bind(&alt_gen->possible_success);
|
| - new_variant.set_quick_check_performed(&alt_gen->quick_check_details);
|
| - new_variant.set_characters_preloaded(preload_characters);
|
| - new_variant.set_bound_checked_up_to(preload_characters);
|
| + new_trace.set_quick_check_performed(&alt_gen->quick_check_details);
|
| + new_trace.set_characters_preloaded(preload_characters);
|
| + new_trace.set_bound_checked_up_to(preload_characters);
|
| generate_full_check_inline = true;
|
| }
|
| } else {
|
| @@ -2936,18 +2930,18 @@
|
| // to generate probably can't use it.
|
| if (i != first_normal_choice) {
|
| alt_gen->expects_preload = false;
|
| - new_variant.set_characters_preloaded(0);
|
| + new_trace.set_characters_preloaded(0);
|
| }
|
| if (i < choice_count - 1) {
|
| - new_variant.set_backtrack(&alt_gen->after);
|
| + new_trace.set_backtrack(&alt_gen->after);
|
| }
|
| generate_full_check_inline = true;
|
| }
|
| if (generate_full_check_inline) {
|
| for (int j = 0; j < guard_count; j++) {
|
| - GenerateGuard(macro_assembler, guards->at(j), &new_variant);
|
| + GenerateGuard(macro_assembler, guards->at(j), &new_trace);
|
| }
|
| - if (!alternative.node()->Emit(compiler, &new_variant)) {
|
| + if (!alternative.node()->Emit(compiler, &new_trace)) {
|
| greedy_loop_label.Unuse();
|
| return false;
|
| }
|
| @@ -2958,7 +2952,7 @@
|
| if (greedy_loop) {
|
| macro_assembler->Bind(&greedy_loop_label);
|
| // If we have unwound to the bottom then backtrack.
|
| - macro_assembler->CheckGreedyLoop(variant->backtrack());
|
| + macro_assembler->CheckGreedyLoop(trace->backtrack());
|
| // Otherwise try the second priority at an earlier position.
|
| macro_assembler->AdvanceCurrentPosition(-text_length);
|
| macro_assembler->GoTo(&second_choice);
|
| @@ -2969,7 +2963,7 @@
|
| for (int i = first_normal_choice; i < choice_count - 1; i++) {
|
| AlternativeGeneration* alt_gen = alt_gens.at(i);
|
| if (!EmitOutOfLineContinuation(compiler,
|
| - current_variant,
|
| + current_trace,
|
| alternatives_->at(i),
|
| alt_gen,
|
| preload_characters,
|
| @@ -2982,7 +2976,7 @@
|
|
|
|
|
| bool ChoiceNode::EmitOutOfLineContinuation(RegExpCompiler* compiler,
|
| - GenerationVariant* variant,
|
| + Trace* trace,
|
| GuardedAlternative alternative,
|
| AlternativeGeneration* alt_gen,
|
| int preload_characters,
|
| @@ -2991,41 +2985,41 @@
|
|
|
| RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
|
| macro_assembler->Bind(&alt_gen->possible_success);
|
| - GenerationVariant out_of_line_variant(*variant);
|
| - out_of_line_variant.set_characters_preloaded(preload_characters);
|
| - out_of_line_variant.set_quick_check_performed(&alt_gen->quick_check_details);
|
| + Trace out_of_line_trace(*trace);
|
| + out_of_line_trace.set_characters_preloaded(preload_characters);
|
| + out_of_line_trace.set_quick_check_performed(&alt_gen->quick_check_details);
|
| ZoneList<Guard*>* guards = alternative.guards();
|
| int guard_count = (guards == NULL) ? 0 : guards->length();
|
| if (next_expects_preload) {
|
| Label reload_current_char;
|
| - out_of_line_variant.set_backtrack(&reload_current_char);
|
| + out_of_line_trace.set_backtrack(&reload_current_char);
|
| for (int j = 0; j < guard_count; j++) {
|
| - GenerateGuard(macro_assembler, guards->at(j), &out_of_line_variant);
|
| + GenerateGuard(macro_assembler, guards->at(j), &out_of_line_trace);
|
| }
|
| - bool ok = alternative.node()->Emit(compiler, &out_of_line_variant);
|
| + bool ok = alternative.node()->Emit(compiler, &out_of_line_trace);
|
| macro_assembler->Bind(&reload_current_char);
|
| // Reload the current character, since the next quick check expects that.
|
| // We don't need to check bounds here because we only get into this
|
| // code through a quick check which already did the checked load.
|
| - macro_assembler->LoadCurrentCharacter(variant->cp_offset(),
|
| + macro_assembler->LoadCurrentCharacter(trace->cp_offset(),
|
| NULL,
|
| false,
|
| preload_characters);
|
| macro_assembler->GoTo(&(alt_gen->after));
|
| return ok;
|
| } else {
|
| - out_of_line_variant.set_backtrack(&(alt_gen->after));
|
| + out_of_line_trace.set_backtrack(&(alt_gen->after));
|
| for (int j = 0; j < guard_count; j++) {
|
| - GenerateGuard(macro_assembler, guards->at(j), &out_of_line_variant);
|
| + GenerateGuard(macro_assembler, guards->at(j), &out_of_line_trace);
|
| }
|
| - return alternative.node()->Emit(compiler, &out_of_line_variant);
|
| + return alternative.node()->Emit(compiler, &out_of_line_trace);
|
| }
|
| }
|
|
|
|
|
| -bool ActionNode::Emit(RegExpCompiler* compiler, GenerationVariant* variant) {
|
| +bool ActionNode::Emit(RegExpCompiler* compiler, Trace* trace) {
|
| RegExpMacroAssembler* assembler = compiler->macro_assembler();
|
| - LimitResult limit_result = LimitVersions(compiler, variant);
|
| + LimitResult limit_result = LimitVersions(compiler, trace);
|
| if (limit_result == DONE) return true;
|
| if (limit_result == FAIL) return false;
|
| ASSERT(limit_result == CONTINUE);
|
| @@ -3034,58 +3028,58 @@
|
|
|
| switch (type_) {
|
| case STORE_POSITION: {
|
| - GenerationVariant::DeferredCapture
|
| - new_capture(data_.u_position_register.reg, variant);
|
| - GenerationVariant new_variant = *variant;
|
| - new_variant.add_action(&new_capture);
|
| - return on_success()->Emit(compiler, &new_variant);
|
| + Trace::DeferredCapture
|
| + new_capture(data_.u_position_register.reg, trace);
|
| + Trace new_trace = *trace;
|
| + new_trace.add_action(&new_capture);
|
| + return on_success()->Emit(compiler, &new_trace);
|
| }
|
| case INCREMENT_REGISTER: {
|
| - GenerationVariant::DeferredIncrementRegister
|
| + Trace::DeferredIncrementRegister
|
| new_increment(data_.u_increment_register.reg);
|
| - GenerationVariant new_variant = *variant;
|
| - new_variant.add_action(&new_increment);
|
| - return on_success()->Emit(compiler, &new_variant);
|
| + Trace new_trace = *trace;
|
| + new_trace.add_action(&new_increment);
|
| + return on_success()->Emit(compiler, &new_trace);
|
| }
|
| case SET_REGISTER: {
|
| - GenerationVariant::DeferredSetRegister
|
| + Trace::DeferredSetRegister
|
| new_set(data_.u_store_register.reg, data_.u_store_register.value);
|
| - GenerationVariant new_variant = *variant;
|
| - new_variant.add_action(&new_set);
|
| - return on_success()->Emit(compiler, &new_variant);
|
| + Trace new_trace = *trace;
|
| + new_trace.add_action(&new_set);
|
| + return on_success()->Emit(compiler, &new_trace);
|
| }
|
| case CLEAR_CAPTURES: {
|
| - GenerationVariant::DeferredClearCaptures
|
| + Trace::DeferredClearCaptures
|
| new_capture(Interval(data_.u_clear_captures.range_from,
|
| data_.u_clear_captures.range_to));
|
| - GenerationVariant new_variant = *variant;
|
| - new_variant.add_action(&new_capture);
|
| - return on_success()->Emit(compiler, &new_variant);
|
| + Trace new_trace = *trace;
|
| + new_trace.add_action(&new_capture);
|
| + return on_success()->Emit(compiler, &new_trace);
|
| }
|
| case BEGIN_SUBMATCH:
|
| - if (!variant->is_trivial()) return variant->Flush(compiler, this);
|
| + if (!trace->is_trivial()) return trace->Flush(compiler, this);
|
| assembler->WriteCurrentPositionToRegister(
|
| data_.u_submatch.current_position_register, 0);
|
| assembler->WriteStackPointerToRegister(
|
| data_.u_submatch.stack_pointer_register);
|
| - return on_success()->Emit(compiler, variant);
|
| + return on_success()->Emit(compiler, trace);
|
| case EMPTY_MATCH_CHECK: {
|
| int start_pos_reg = data_.u_empty_match_check.start_register;
|
| int stored_pos = 0;
|
| int rep_reg = data_.u_empty_match_check.repetition_register;
|
| bool has_minimum = (rep_reg != RegExpCompiler::kNoRegister);
|
| - bool know_dist = variant->GetStoredPosition(start_pos_reg, &stored_pos);
|
| - if (know_dist && !has_minimum && stored_pos == variant->cp_offset()) {
|
| + bool know_dist = trace->GetStoredPosition(start_pos_reg, &stored_pos);
|
| + if (know_dist && !has_minimum && stored_pos == trace->cp_offset()) {
|
| // If we know we haven't advanced and there is no minimum we
|
| // can just backtrack immediately.
|
| - assembler->GoTo(variant->backtrack());
|
| + assembler->GoTo(trace->backtrack());
|
| return true;
|
| - } else if (know_dist && stored_pos < variant->cp_offset()) {
|
| + } else if (know_dist && stored_pos < trace->cp_offset()) {
|
| // If we know we've advanced we can generate the continuation
|
| // immediately.
|
| - return on_success()->Emit(compiler, variant);
|
| + return on_success()->Emit(compiler, trace);
|
| }
|
| - if (!variant->is_trivial()) return variant->Flush(compiler, this);
|
| + if (!trace->is_trivial()) return trace->Flush(compiler, this);
|
| Label skip_empty_check;
|
| // If we have a minimum number of repetitions we check the current
|
| // number first and skip the empty check if it's not enough.
|
| @@ -3096,12 +3090,12 @@
|
| // If the match is empty we bail out, otherwise we fall through
|
| // to the on-success continuation.
|
| assembler->IfRegisterEqPos(data_.u_empty_match_check.start_register,
|
| - variant->backtrack());
|
| + trace->backtrack());
|
| assembler->Bind(&skip_empty_check);
|
| - return on_success()->Emit(compiler, variant);
|
| + return on_success()->Emit(compiler, trace);
|
| }
|
| case POSITIVE_SUBMATCH_SUCCESS:
|
| - if (!variant->is_trivial()) return variant->Flush(compiler, this);
|
| + if (!trace->is_trivial()) return trace->Flush(compiler, this);
|
| // TODO(erikcorry): Implement support.
|
| if (info()->follows_word_interest ||
|
| info()->follows_newline_interest ||
|
| @@ -3113,14 +3107,14 @@
|
| // Load current character jumps to the label if we are beyond the string
|
| // end.
|
| assembler->LoadCurrentCharacter(0, &at_end);
|
| - assembler->GoTo(variant->backtrack());
|
| + assembler->GoTo(trace->backtrack());
|
| assembler->Bind(&at_end);
|
| }
|
| assembler->ReadCurrentPositionFromRegister(
|
| data_.u_submatch.current_position_register);
|
| assembler->ReadStackPointerFromRegister(
|
| data_.u_submatch.stack_pointer_register);
|
| - return on_success()->Emit(compiler, variant);
|
| + return on_success()->Emit(compiler, trace);
|
| default:
|
| UNREACHABLE();
|
| return false;
|
| @@ -3128,14 +3122,13 @@
|
| }
|
|
|
|
|
| -bool BackReferenceNode::Emit(RegExpCompiler* compiler,
|
| - GenerationVariant* variant) {
|
| +bool BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
|
| RegExpMacroAssembler* assembler = compiler->macro_assembler();
|
| - if (!variant->is_trivial()) {
|
| - return variant->Flush(compiler, this);
|
| + if (!trace->is_trivial()) {
|
| + return trace->Flush(compiler, this);
|
| }
|
|
|
| - LimitResult limit_result = LimitVersions(compiler, variant);
|
| + LimitResult limit_result = LimitVersions(compiler, trace);
|
| if (limit_result == DONE) return true;
|
| if (limit_result == FAIL) return false;
|
| ASSERT(limit_result == CONTINUE);
|
| @@ -3148,16 +3141,16 @@
|
| // iff the back reference is empty.
|
| assembler->CheckNotRegistersEqual(start_reg_,
|
| end_reg_,
|
| - variant->backtrack());
|
| + trace->backtrack());
|
| } else {
|
| if (compiler->ignore_case()) {
|
| assembler->CheckNotBackReferenceIgnoreCase(start_reg_,
|
| - variant->backtrack());
|
| + trace->backtrack());
|
| } else {
|
| - assembler->CheckNotBackReference(start_reg_, variant->backtrack());
|
| + assembler->CheckNotBackReference(start_reg_, trace->backtrack());
|
| }
|
| }
|
| - return on_success()->Emit(compiler, variant);
|
| + return on_success()->Emit(compiler, trace);
|
| }
|
|
|
|
|
|
|